Android RecyclerView未按预期滚动
我有一个项目,我使用一个水平的回收视图,我想中心一个元素。我的实现工作正常,但并非在所有情况下都可以在此处检查此GIF: 你可能会注意到,如果我从左边来,它会正确地滚动。如果我是右翼的话,我会犯很多错误,我不知道如何停止,也不知道如何解决 我将代码分条到以下示例:Android RecyclerView未按预期滚动,android,android-animation,smooth-scrolling,android-recyclerview,Android,Android Animation,Smooth Scrolling,Android Recyclerview,我有一个项目,我使用一个水平的回收视图,我想中心一个元素。我的实现工作正常,但并非在所有情况下都可以在此处检查此GIF: 你可能会注意到,如果我从左边来,它会正确地滚动。如果我是右翼的话,我会犯很多错误,我不知道如何停止,也不知道如何解决 我将代码分条到以下示例: public class DemoActivity extends ActionBarActivity implements View.OnClickListener { private static final int J
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);
}
}
}
这是我的适配器:
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);
}
}
}
公共类MyAdapter扩展了RecyclerView。适配器实现了View.OnClickListener{
公共静态最终整数可见项=7;
公共静态最终整数不可见项=150;
私有静态最终整数合计项目=可见项目+不可见项目*2;
公共静态final int ITEM_IN_CENTER=(int)Math.ceil(VISIBLE_ITEMS/2f)+非VISIBLE_ITEMS;
私人日历;
公共MyAdapter(){
mCalendar=GregorianCalendar.getInstance();
setHasStableIds(true);
}
private int getToday(){
return(int)TimeUnit.millises.toDays(System.currentTimeMillis());
}
@凌驾
public int getItemCount(){
返回总项目;
}
@凌驾
public Holder onCreateViewHolder(视图组父级,int-viewType){
final TextView tv=新的TextView(parent.getContext());
int width=parent.getWidth()/VISIBLE\u ITEMS;
tv.setLayoutParams(新的TableRow.LayoutParams(宽度,ViewGroup.LayoutParams.MATCH_父项,1));
电视。设置重力(重力。重心);
tv.setBackgroundColor(彩色透明);
DisplayMetrics=tv.getContext().getResources().getDisplayMetrics();
浮动填充=TypedValue.applyDimension(TypedValue.COMPLEX\u UNIT\u DIP,10,度量);
tv.设置行距(填充,1f);
设置填充(0,(int)填充,0,0);
tv.setOnClickListener(此);
归还新的支架(电视);
}
@凌驾
公共无效onBindViewHolder(Holder Holder,内部位置){
int today=getToday();
mCalendar.setTimeInMillis(System.currentTimeMillis());
mCalendar.set(Calendar.HOUR OF_DAY,12);//设置为中午,以避免节能时间问题
mCalendar.add(Calendar.DAY\u OF\u YEAR,position-ITEM\u IN\u CENTER+1);
DateFormat格式=新的SimpleDataFormat(“E\nd”);
String label=format.format(mCalendar.getTime()).replace(“.\n”和“\n”);
int day=(int)TimeUnit.millises.toDays(mCalendar.getTimeInMillis());
持有者。更新(日期、今天、标签);
}
@凌驾
公共长getItemId(int位置){
mCalendar.setTimeInMillis(System.currentTimeMillis());
mCalendar.set(Calendar.HOUR OF_DAY,12);//设置为中午,以避免节能时间问题
mCalendar.add(Calendar.DAY\u OF\u YEAR,position-ITEM\u IN\u CENTER+1);
DateFormat格式=新的SimpleDataFormat(“dMMyyyy”);
返回Long.parseLong(format.format(mCalendar.getTime());
}
@凌驾
公共void onClick(视图v){
字符串day=((TextView)v.getText().toString().replace(“\n”,”);
Toast.makeText(v.getContext(),“您点击了”+day,Toast.LENGTH\u SHORT.show();
}
公共类持有者扩展了RecyclerView.ViewHolder{
专用最终字体;
私人持有人(TextView v){
超级(五);
if(Build.VERSION.SDK\u INT>=Build.VERSION\u代码.冰淇淋\u三明治){
字体=字体。创建(“无衬线光”,字体。普通);
}否则{
font=null;
}
}
公共无效更新(整数日、整数日、字符串标签){
TextView tv=(TextView)项目视图;
tv.setText(标签);
如果(天==今天){
电视节目(18),;
tv.setTypeface(null,Typeface.BOLD);
}否则{
电视节目(16),;
设置字体(字体、字体、普通);
}
电视背景色(0xff8dc380);
}
}
}
你觉得有什么原因吗?为了让您更简单,我还将此代码放在GitHub上 我发现了一个令人惊讶的简单解决方法:
@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);
}
}
这里我自己计算跳跃的宽度,这正是我想要的
要支持平滑滚动,必须覆盖
smoothScrollToPosition(RecyclerView、State、int)并创建
RecyclerView.SmoothScroller
RecyclerView.LayoutManager负责
用于创建实际的滚动动作。如果您想提供自定义
平滑滚动逻辑,覆盖平滑滚动位置(RecyclerView,
在LayoutManager中声明,int)
在您的情况下,使用smoothScrollBy可能是一种解决方法(不需要此替代)。在使用垂直方向的LinearLayoutManager的情况下,您可以创建自己的SmoothScroller并替代CalculatedyToMakeMakeVisible()方法,从中可以设置所需的视图位置。例如,要使目标视图在smoothScroll()之后始终显示在RecyclerView的顶部,请编写以下命令:
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);
}
“顶部”和“底部”-目标视图的边界
“开始”和“结束”-在smoothScroll过程中视图应该放置在这两个点之间这对我很有用:
itemsView.smoothScrollBy(-recyclerView.computeHorizontalScrollOffset(), 0)
您可以尝试使用FancyCoverFlow作为水平选择器。它就像viewpager@berserk该库使用了我不想使用的已弃用的
GalleryView
。请将那些SimpleDataFormat作为字段,每个视图创建两个循环太多了。HI@rekire:ForecasAdapter.ITEM\u在这里的\u中心是什么?在我的例子中,它是151,因为我有301个元素。回答很好。。。。没有完全解决问题,但使滚动更好。我通过向int-top添加+1000来修复它。不会推荐它,因为它是一个丑陋的黑客,但对于那些有同样问题的人,这将是方案C或D。对于垂直,这为我做到了:linearLayoutManager.scrollToPositionWithOffset(位置,0);