当前位置:网站首页>(原创)自定义一个滚屏的RecyclerView
(原创)自定义一个滚屏的RecyclerView
2022-07-25 14:34:00 【Android_xiong_st】
效果展示
RecyclerView可以自己定制成想要的各种样式
比如首页的Banner,卡片样式等等
今天来分享一个滚动的RecyclerView,先看下效果:
下面就具体讲解下如何实现
Adapter
首先是adapter,因为要实现无限滚动,所以在getItemCount方法返回Integer.MAX_VALUE
具体代码如下:
public class AutoPollAdapter extends RecyclerView.Adapter {
/** * 事件回调监听 */
private OnItemClickListener onItemClickListener;
private Context context;
private List<AutoBean> listBeans;
public AutoPollAdapter(Context context, List<AutoBean> listBeans) {
this.context = context;
this.listBeans = listBeans;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 实例化展示的view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_auto_poll, parent, false);
// 实例化viewholder
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.imgHead = view.findViewById(R.id.img_head);
viewHolder.tvName = view.findViewById(R.id.tv_name);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder holder1 = (ViewHolder) holder;
holder1.tvName.setText(listBeans.get(position % listBeans.size()).tvname);
Glide.with(context).load(listBeans.get(position % listBeans.size()).url).into(holder1.imgHead);
holder1.tvName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick((position % listBeans.size()));
}
});
}
@Override
public int getItemCount() {
//主要在这,实现无线轮播效果
return Integer.MAX_VALUE;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
ImageView imgHead;
public ViewHolder(View itemView) {
super(itemView);
}
}
/** * 设置回调监听 * * @param listener */
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public interface OnItemClickListener {
void onItemClick(int position);
}
}
相关的xml也bean也贴上:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="horizontal" android:gravity="center_vertical" android:layout_height="60dp">
<ImageView android:id="@+id/img_head" android:layout_width="56dp" android:layout_height="56dp" android:src="@drawable/ic_launcher" />
<TextView android:id="@+id/tv_name" android:text="测试" android:layout_marginLeft="10dp" android:layout_width="match_parent" android:layout_height="wrap_content" />
</LinearLayout>
public class AutoBean {
String tvname;
String url;
public AutoBean(String tvname, String url) {
this.tvname = tvname;
this.url = url;
}
}
自定义的LinearLayoutManager
自定义一个layoutManager,去控制滑动的速度:
public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager {
private float MILLISECONDS_PER_INCH = 0.03f;
private Context contxt;
public ScrollSpeedLinearLayoutManger(Context context) {
super(context);
this.contxt = context;
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return ScrollSpeedLinearLayoutManger.this
.computeScrollVectorForPosition(targetPosition);
}
@Override
protected float calculateSpeedPerPixel
(DisplayMetrics displayMetrics) {
setSpeedSlow();
return MILLISECONDS_PER_INCH / displayMetrics.density;
// return 7;
// 返回滑动一个pixel需要多少毫秒
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
public void setSpeedSlow() {
//自己在这里用density去乘,希望不同分辨率设备上滑动速度相同
//0.3f是自己估摸的一个值,可以根据不同需求自己修改
MILLISECONDS_PER_INCH = contxt.getResources().getDisplayMetrics().density * 3f;
}
public void setSpeedFast() {
MILLISECONDS_PER_INCH = contxt.getResources().getDisplayMetrics().density * 0.03f;
}
}
自定义的RecyclerView
然后贴上自己的RecyclerView:
public class AutoPollRecyclerView extends RecyclerView {
private static final long TIME_AUTO_POLL_1 = 2000;
AutoPollTask autoPollTask;
private int index = 2;//这个值很重要,动画先滑动到第几个
private boolean running; //标示是否正在自动轮询
private boolean canRun;//标示是否可以自动轮询,可在不需要的是否置false
public AutoPollRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
autoPollTask = new AutoPollTask(this);
}
/*** * 一次只能滑一个item(轮播图) */
static class AutoPollTask implements Runnable {
private final WeakReference<AutoPollRecyclerView> mReference;
//使用弱引用持有外部类引用->防止内存泄漏
public AutoPollTask(AutoPollRecyclerView reference) {
this.mReference = new WeakReference<AutoPollRecyclerView>(reference);
}
@Override
public void run() {
AutoPollRecyclerView recyclerView = mReference.get();
if (recyclerView != null && recyclerView.running && recyclerView.canRun) {
recyclerView.smoothScrollToPosition(++recyclerView.index);
recyclerView.postDelayed(recyclerView.autoPollTask, TIME_AUTO_POLL_1);
}
}
}
//开启:如果正在运行,先停止->再开启
public void start() {
if (running)
stop();
canRun = true;
running = true;
postDelayed(autoPollTask, TIME_AUTO_POLL_1);
}
public void stop() {
running = false;
removeCallbacks(autoPollTask);
}
// 实现渐变效果
Paint mPaint;
private int layerId;
private LinearGradient linearGradient;
/** * 禁止滑动 * @param e * @return */
public boolean onTouchEvent(MotionEvent e) {
return true;
}
/** * 禁止滑动 * @param e * @return */
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return false;
}
private int preWidth = 0;// Recyclerview宽度动态变化时,监听每一次的宽度
public void doTopGradualEffect(final int itemViewWidth) {
mPaint = new Paint();
// dst_in 模式,实现底层透明度随上层透明度进行同步显示(即上层为透明时,下层就透明,并不是上层覆盖下层)
final Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
mPaint.setXfermode(xfermode);
addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(canvas, parent, state);
// 当linearGradient为空即第一次绘制 或 Recyclerview宽度发生改变时,重新计算透明位置
if (linearGradient == null || preWidth != parent.getWidth()) {
// 透明位置从最后一个 itemView 的一半处到 Recyclerview 的最右边
linearGradient = new LinearGradient(parent.getWidth() - (itemViewWidth / 2), 0.0f, parent.getWidth(), 0.0f, new int[]{
Color.BLACK, 0}, null, Shader.TileMode.CLAMP);
preWidth = parent.getWidth();
}
mPaint.setXfermode(xfermode);
mPaint.setShader(linearGradient);
canvas.drawRect(0.0f, 0.0f, parent.getRight(), parent.getBottom(), mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 此处 Paint的参数这里传的null, 在传入 mPaint 时会出现第一次打开黑屏闪现的问题
// 注意 saveLayer 不能省也不能移动到onDrawOver方法里
layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
});
}
}
这里要注意一点,因为我们的滚屏,是不想让他可以通过手指去滑动的
也就是禁用RecyclerView的手指滑动
一般解决方案是重写layoutManager的两个方法:
canScrollVertically和canScrollHorizontally
返回false,分别禁用左右滑动和上下滑动
但这样会带来一个新的问题:
RecyclerVIew使用这个方法禁止滑动后,通过smoothScrollToPosition滑动到指定item的方法也失效了
这样自动滚屏效果就没了
我这边没用这个方法,用的是重写RecyclerView的下面两个方法:
/** * 禁止滑动 * @param e * @return */
public boolean onTouchEvent(MotionEvent e) {
return true;
}
/** * 禁止滑动 * @param e * @return */
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
return false;
}
所以如果要打开手指滑动,注释这两个方法,然后记录监听滑动到的位置,重新设置自动滑动(这点很重要)
这个大家可以自己去实现
使用
最后,贴上使用的代码:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.mydemo.recy.AutoPollRecyclerView
android:id="@+id/recycleView"
android:fadingEdge="vertical"
android:scrollbars="none"
android:fadingEdgeLength="10dp"
android:layout_width="match_parent"
tools:listitem="@layout/item_auto_poll"
android:layout_height="180dp"/>
</FrameLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_recy);
ScrollSpeedLinearLayoutManger layoutManager = new ScrollSpeedLinearLayoutManger(this);
layoutManager.setSmoothScrollbarEnabled(true);
layoutManager.setAutoMeasureEnabled(true);
AutoPollRecyclerView recyclerView = findViewById(R.id.recycleView);
recyclerView.setLayoutManager(layoutManager);// 布局管理器。
recyclerView.setHasFixedSize(true);// 如果Item够简单,高度是确定的,打开FixSize将提高性能。
recyclerView.setItemAnimator(new DefaultItemAnimator());// 设置Item默认动画,加也行,不加也行。
ArrayList<AutoBean> list = new ArrayList<>();
list.add(new AutoBean("哆啦A梦","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2F91%2F81%2Fd7%2F9181d7d8787f7e96f0017da5e61ad27d.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659497299&t=05f317ff452b3f1a452f7e06c9ce8703"));
list.add(new AutoBean("小狗","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201610%2F17%2F20161017213843_EeViB.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659497393&t=6f8f90c5b409231ef5902aafc7464d31"));
list.add(new AutoBean("卡通头像","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202107%2F09%2F20210709142454_dc8dc.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659497417&t=1a38f19555bcb59f5f9e127126fba84a"));
list.add(new AutoBean("火影头像","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_bt%2F0%2F13207652291%2F1000.jpg&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659497813&t=a911ff64c37a57aeb95a6f31d13a573b"));
AutoPollAdapter autoPollAdapter = new AutoPollAdapter(MainActivity.this, list);
autoPollAdapter.setOnItemClickListener(new AutoPollAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Log.d("AutoPollAdapter", "点击了" + (position));
Toast.makeText(MainActivity.this, "点击了" + (position ), Toast.LENGTH_SHORT).show();
}
});
recyclerView.setAdapter(autoPollAdapter);
recyclerView.start();
DisplayMetrics dm = getResources().getDisplayMetrics();
recyclerView.doTopGradualEffect(dm.widthPixels);
}
}
最后注意一点:
如果要recycleview里面显示的item数量为整数
比如就显示三个,
那么recycleview的高度,一定要设置成item的高度的整倍数
比如三倍,就代表可以显示三个item,五倍就显示五个。
边栏推荐
- QObject source code analysis -d pointer and Q pointer
- Application practice: Great integrator of the paddy classification model [paddlehub, finetune, prompt]
- Ten common application scenarios of redis
- Deep understanding of pytorch distributed parallel processing tool DDP -- starting from bugs in engineering practice
- 结构体大小
- 【口才】谈判说服技巧及策略
- ~4.1 sword finger offer 05. replace spaces
- 安防市场进入万亿时代,安防B2B网上商城平台精准对接深化企业发展路径
- 疫情之下,生物医药行业或将迎来突破性发展
- Under the epidemic, the biomedical industry may usher in breakthrough development
猜你喜欢

PHP website design ideas

【MySQL必知必会】触发器 | 权限管理

阿里云安装MYSQL5.7

DVWA practice - brute force cracking

sudo rosdep init Error ROS安装问题解决方案

Development of uni app offline ID card identification plug-in based on paddleocr

基于浏览器的分屏阅读

sqli-labs Basic Challenges Less1-10
Famous handwritten note taking software recruit CTO · coordinate Shenzhen

sqli-labs Basic Challenges Less11-22
随机推荐
Gson and fastjson
Why do China Construction and China Railway need this certificate? What is the reason?
Polymorphism and interface
Oka pass rights and interests analysis is the best choice to participate in okaleido ecological construction
Throwing OutOfMemoryError “Could not allocate JNI Env“
【cartographer_ros】八: 官方Demo参数配置和效果
thymeleaf通过样式控制display是否显示
基于PaddleOCR开发uni-app离线身份证识别插件
opencv视频跟踪「建议收藏」
基于浏览器的分屏阅读
【口才】谈判说服技巧及策略
Filters get the data in data; Filters use data in data
华为ensp路由器静态路由(默认路由的下一跳地址)
QObject source code analysis -d pointer and Q pointer
MySQL table operation
用GaussDB(for Redis)存画像,推荐业务轻松降本60%
Dpkg package download addresses of various platforms (including arm64)
Two Sum
Go语言创始人从Google离职
【MySQL必知必会】触发器 | 权限管理