27

Ich habe ein Projekt, wo ich eine horizontale Recycler-Ansicht verwende und ich möchte ein Element zentrieren. Meine Implementierung funktioniert, aber nicht in jedem Fall diese GIF hier überprüfen:RecyclerView scrollt nicht wie erwartet

Wie Sie feststellen, kann es scrollt richtig, wenn ich von links kommen. Wenn ich von rechts komme, überspringt es sehr viel und ich habe keine Ahnung, wie ich aufhören oder reparieren kann.

gestreift ich meinen Code auf dieses Beispiel hier:

public class DemoActivity extends ActionBarActivity implements View.OnClickListener { 
    private static final int JUMP_TO_LEFT = MyAdapter.NON_VISIBLE_ITEMS + MyAdapter.VISIBLE_ITEMS - 1; 
    private static final int JUMP_TO_RIGHT = MyAdapter.NON_VISIBLE_ITEMS; 
    private LinearLayoutManager mLayoutManager; 
    private RecyclerView mRecycler; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_demo); 
     findViewById(android.R.id.button1).setOnClickListener(this); 
     mRecycler = (RecyclerView)findViewById(R.id.recycler); 
     MyAdapter mAdapter = new MyAdapter(); 
     mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); 
     mRecycler.setLayoutManager(mLayoutManager); 
     mRecycler.setHasFixedSize(true); 
     mRecycler.scrollToPosition(MyAdapter.NON_VISIBLE_ITEMS); 
     mRecycler.setAdapter(mAdapter); 
    } 

    @Override 
    public void onClick(View v) { 
     int pos = mLayoutManager.findFirstVisibleItemPosition(); 
     int outer = (MyAdapter.VISIBLE_ITEMS - 1)/2; 
     if(pos + outer >= MyAdapter.ITEM_IN_CENTER) { 
      mRecycler.smoothScrollToPosition(JUMP_TO_RIGHT); 
     } else { 
      mRecycler.smoothScrollToPosition(JUMP_TO_LEFT); 
     } 
    } 
} 

Und hier ist mein Adapter:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Holder> implements View.OnClickListener { 
    public static final int VISIBLE_ITEMS = 7; 
    public static final int NON_VISIBLE_ITEMS = 150; 
    private static final int TOTAL_ITEMS = VISIBLE_ITEMS + NON_VISIBLE_ITEMS * 2; 
    public static final int ITEM_IN_CENTER = (int)Math.ceil(VISIBLE_ITEMS/2f) + NON_VISIBLE_ITEMS; 

    private Calendar mCalendar; 

    public MyAdapter() { 
     mCalendar = GregorianCalendar.getInstance(); 
     setHasStableIds(true); 
    } 

    private int getToday() { 
     return (int)TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis()); 
    } 

    @Override 
    public int getItemCount() { 
     return TOTAL_ITEMS; 
    } 

    @Override 
    public Holder onCreateViewHolder(ViewGroup parent, int viewType) { 
     final TextView tv = new TextView(parent.getContext()); 
     int width = parent.getWidth()/VISIBLE_ITEMS; 
     tv.setLayoutParams(new TableRow.LayoutParams(width, ViewGroup.LayoutParams.MATCH_PARENT, 1)); 
     tv.setGravity(Gravity.CENTER); 
     tv.setBackgroundColor(Color.TRANSPARENT); 
     DisplayMetrics metrics = tv.getContext().getResources().getDisplayMetrics(); 
     float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, metrics); 
     tv.setLineSpacing(padding, 1f); 
     tv.setPadding(0, (int)padding, 0, 0); 
     tv.setOnClickListener(this); 
     return new Holder(tv); 
    } 

    @Override 
    public void onBindViewHolder(Holder holder, int position) { 
     int today = getToday(); 
     mCalendar.setTimeInMillis(System.currentTimeMillis()); 
     mCalendar.set(Calendar.HOUR_OF_DAY, 12); // set to noon to avoid energy saver time problems 
     mCalendar.add(Calendar.DAY_OF_YEAR, position - ITEM_IN_CENTER + 1); 
     DateFormat format = new SimpleDateFormat("E\nd"); 
     String label = format.format(mCalendar.getTime()).replace(".\n", "\n"); 
     int day = (int)TimeUnit.MILLISECONDS.toDays(mCalendar.getTimeInMillis()); 
     holder.update(day, today, label); 
    } 

    @Override 
    public long getItemId(int position) { 
     mCalendar.setTimeInMillis(System.currentTimeMillis()); 
     mCalendar.set(Calendar.HOUR_OF_DAY, 12); // set to noon to avoid energy saver time problems 
     mCalendar.add(Calendar.DAY_OF_YEAR, position - ITEM_IN_CENTER + 1); 
     DateFormat format = new SimpleDateFormat("dMMyyyy"); 
     return Long.parseLong(format.format(mCalendar.getTime())); 
    } 

    @Override 
    public void onClick(View v) { 
     String day = ((TextView)v).getText().toString().replace("\n", " "); 
     Toast.makeText(v.getContext(), "You clicked on " + day, Toast.LENGTH_SHORT).show(); 
    } 

    public class Holder extends RecyclerView.ViewHolder { 
     private final Typeface font; 

     private Holder(TextView v) { 
      super(v); 
      if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 
       font = Typeface.create("sans-serif-light", Typeface.NORMAL); 
      } else { 
       font = null; 
      } 
     } 

     public void update(int day, int today, String label) { 
      TextView tv = (TextView)itemView; 
      tv.setText(label); 

      if(day == today) { 
       tv.setTextSize(18); 
       tv.setTypeface(null, Typeface.BOLD); 
      } else { 
       tv.setTextSize(16); 
       tv.setTypeface(font, Typeface.NORMAL); 
      } 

      tv.setBackgroundColor(0xff8dc380); 
     } 
    } 
} 

Sehen Sie einen Grund dafür? Um es für Sie einfacher zu machen, habe ich diesen Code auch auf GitHub gesetzt. https://github.com/rekire/RecylcerViewBug

+0

Sie können für die horizontalen Wähler FancyCoverFlow versuchen. Es funktioniert wie viewpager – berserk

+1

@berserk, dass lib die veraltete 'GalleryView' verwendet, die ich nicht verwenden möchte. – rekire

+0

Bitte legen Sie diese SimpleDateFormat als Felder, Erstellen von zwei pro Ansicht Recycling ist zu viel. –

Antwort

14

fand ich eine überraschend einfache Abhilfe:

@Override 
public void onClick(View v) { 
    int pos = mLayoutManager.findFirstVisibleItemPosition(); 
    int outer = (MyAdapter.VISIBLE_ITEMS + 1)/2; 
    int delta = pos + outer - ForecastAdapter.ITEM_IN_CENTER; 
    //Log.d("Scroll", "delta=" + delta); 
    View firstChild = mForecast.getChildAt(0); 
    if(firstChild != null) { 
     mForecast.smoothScrollBy(firstChild.getWidth() * -delta, 0); 
    } 
} 

Hier berechne ich die Breite selbst zu springen, das genau das tut, was ich will.

+0

HI @rekire: Was ist ForecastAdapter.ITEM_IN_CENTER hier? – Beena

+0

In meinem Fall ist es 151, seit ich 301 Elemente habe. – rekire

0

smooth scrolling zu unterstützen, müssen Sie smoothScrollToPosition (RecyclerView, Staat, int) und erstellen Sie eine RecyclerView.SmoothScroller außer Kraft setzen.

RecyclerView.LayoutManager ist verantwortlich für die Erstellung der eigentlichen Scroll-Aktion . Wenn Sie eine benutzerdefinierte Smooth-Scroll-Logik bereitstellen möchten, überschreiben Sie SmoothScrollToPosition (RecyclerView, State, int) in Ihrem LayoutManager.

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#smoothScrollToPosition(int)

In Ihrem Fall verwenden smoothScrollBy eine Abhilfe könnte (muss nicht mit dieser Überschreibung).

5

Bei LinearLayoutManager mit vertikaler Ausrichtung können Sie einen eigenen SmoothScroller erstellen und die calculateDyToMakeVisible() -Methode überschreiben, in der Sie die gewünschte Ansichtsposition festlegen können. Zum Beispiel, um die Zielansicht zu machen immer auf der Oberseite RecyclerView nach Smooth zu sein scheint() schreibt dies:

class CustomLinearSmoothScroller extends LinearSmoothScroller { 

    public CustomLinearSmoothScroller(Context context) { 
     super(context); 
    } 

    @Override 
    public int calculateDyToMakeVisible(View view, int snapPreference) { 
     final RecyclerView.LayoutManager layoutManager = getLayoutManager(); 
     if (!layoutManager.canScrollVertically()) { 
      return 0; 
     } 
     final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)view.getLayoutParams(); 
     final int top = layoutManager.getDecoratedTop(view) - params.topMargin; 
     final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin; 
     final int viewHeight = bottom - top; 
     final int start = layoutManager.getPaddingTop(); 
     final int end = start + viewHeight; 
     return calculateDtToFit(top, bottom, start, end, snapPreference); 
    } 

„oben“ und „unten“ - Grenzen der Zielansicht

" Start“und‚Ende‘- Punkte, zwischen denen sollte die Ansicht

+0

Schöne Antwort .... Das Problem wurde nicht vollständig behoben, aber das Scrollen ist viel besser. Ich habe es behoben, indem ich nur +1000 zu int top hinzugefügt habe.Ich würde es nicht empfehlen, denn es ist ein hässlicher Hack, aber für diejenigen, die das gleiche Problem hatten, wäre dies Plan C oder D. –

+0

für die vertikale, dies tat es für mich: linearLayoutManager.scrollToPositionWithOffset (Position, 0); – MinaHany

0

Dieser arbeitete für mich bei Smooth gestellt werden:

itemsView.smoothScrollBy(-recyclerView.computeHorizontalScrollOffset(), 0)