27

Mam projekt, w którym używam poziomego widoku recyklera i chcę wyśrodkować jeden element. Moja implementacja działa, ale nie w każdym przypadku sprawdzić ten GIF tutaj:RecyclerView nie przewija zgodnie z oczekiwaniami

Jak można zauważyć, że przewija się prawidłowo, jeśli pochodzę z lewej strony. Jeśli pochodzę z prawej strony, przeskakuję bardzo dużo i nie mam pojęcia, jak zatrzymać i jak to naprawić.

I paski mój kod do tego przykładu tutaj:

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); 
     } 
    } 
} 

i tu jest mój 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); 
     } 
    } 
} 

widzisz powód do tego? Aby było to dla ciebie prostsze, umieszczam ten kod na GitHub. https://github.com/rekire/RecylcerViewBug

+0

można spróbować FancyCoverFlow dla selektora poziomej. Działa tak jak viewpager – berserk

+1

@berserk, że lib używa przestarzałego 'GalleryView', którego nie chcę używać. – rekire

+0

Proszę umieścić te SimpleDateFormat jako pola, tworząc dwa na widok recyklingu jest zbyt wiele. –

Odpowiedz

14

znalazłem zaskakującą proste rozwiązania:

@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); 
    } 
} 

Tu obliczyć szerokość skakać sobie, że robi dokładnie to, co chcę.

+0

HI @rekire: Co to jest ForecastAdapter.ITEM_IN_CENTER? – Beena

+0

W moim przypadku jest 151, ponieważ mam 301 elementów. – rekire

0

Aby wspierać płynne przewijanie, należy zastąpić smoothScrollToPosition (RecyclerView, państwa, int) i stworzyć RecyclerView.SmoothScroller.

RecyclerView.LayoutManager odpowiada za stworzenie rzeczywistej czynności przewijania. Jeśli chcesz wprowadzić niestandardową płynną logikę przewijania , zastąp parametr smoothScrollToPosition (RecyclerView, State, int) w swoim menedżerze LayoutManager.

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

W twoim przypadku należy użyć smoothScrollBy może być obejście (nie trzeba tę funkcję).

5

W przypadku LinearLayoutManager z orientacją pionową można utworzyć własny wskaźnik SmoothScroller i zastąpić metodę calculDyToMakeVisible(), w której można ustawić pożądaną pozycję widoku. Na przykład, aby pogląd docelową być zawsze wyglądał w górnej części RecyclerView po smoothScroll() pisać tak:

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); 
    } 

„top” i „dół” - granice widoku docelowym

" start”i«koniec»- punkty, pomiędzy którymi należy umieścić widok podczas smoothScroll

+0

Dobra odpowiedź .... Nie rozwiązał problemu całkowicie, ale znacznie ułatwia przewijanie. Naprawiłem to, dodając +1000 do int top.NIE poleciłbym go, ponieważ jest to brzydki hack, ale dla tych, którzy mieli ten sam problem, byłby to plan C lub D. –

+0

dla pionu, to zrobiło to dla mnie: linearLayoutManager.scrollToPositionWithOffset (pozycja, 0); – MinaHany

0

Ten pracował dla mnie:

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