需求场景来源于用户长时间未操作屏幕,唤出的屏保界面,屏保界面效果底部为横向滚动商品小图,上方为商品大图 ~
友情提示:关于如何监听用户长时间未操作屏幕的功能实现可 GO → go 此处 ~
这应该是去年做的一个简单功能,最初是直接套用的别人代码,但是使用中发现存在一些bug和不足,同时适用场景有限,所以特在修改、补全后记录于此 ~
Look here ~
基础版
此版已具备屏保的基本功能,可直接使用~
功能:
- 支持底部商品无限横向滚动
ps:主要在Adapter getItemCount时使用了Integer.MAX_VALUE,性能可能不太好 ~ - 支持滚动期间动态更换大图
- 支持用户手动滑动底部商品图操作
ps:之前存在的一些bug 均以解决 ~
ScreenAdapter
package nk.com.carouseldemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.List;
/**
* @author MrLiu
* @date 2020/12/10
* desc
*/
public class ScreenAdapter extends RecyclerView.Adapter<ScreenAdapter.ViewHolder> {
private Context context;
private List<Integer> mDataList;
private OnItemClickListener onItemClickListener;
public ScreenAdapter(Context context, List<Integer> dataList) {
this.context = context;
this.mDataList = dataList;
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_screen, parent, false);
final ViewHolder vh = new ViewHolder(view);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
int newPos = position % mDataList.size();
holder.mDisplay.setImageResource(mDataList.get(newPos));
holder.itemView.setTag(position);
}
class ViewHolder extends RecyclerView.ViewHolder {
ImageView mDisplay;
public ViewHolder(View itemView) {
super(itemView);
mDisplay = itemView.findViewById(R.id.iv_display);
}
}
}
item_screen - 可不同
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#4CA9EE"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_display"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:padding="5dp"
tools:src="@mipmap/ic_launcher" />
</LinearLayout>
MainActivity
package nk.com.carouseldemo;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ImageView mDisplay;
RecyclerView mBottomRv;
//此处数据源为项目内的图片,不可套用
private Integer[] mImgIds = {
R.mipmap.banner_1, R.mipmap.banner_2, R.mipmap.banner_3};
private List<Integer> imgLists;
private ScreenAdapter screenAdapter;
private Handler mHandler = new Handler();
private LinearLayoutManager layoutManager;
private int oldItem = 0;
Runnable scrollRunnable = new Runnable() {
@Override
public void run() {
mBottomRv.scrollBy(1, 0);
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem > 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
mHandler.postDelayed(scrollRunnable, 10);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplay = findViewById(R.id.iv_display);
mBottomRv = findViewById(R.id.rv_bottom_screen);
initData();
}
public void initData() {
//模拟数据源
imgLists = new ArrayList<>();
for (int i = 0; i < mImgIds.length; i++) {
imgLists.add(mImgIds[i]);
}
//小优化:正常开发中一般为了用户体验初始的时候先手动加载第一个视图
mDisplay.setImageResource(imgLists.get(0));
screenAdapter = new ScreenAdapter(this, imgLists);
layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mBottomRv.setLayoutManager(layoutManager);
mBottomRv.setAdapter(screenAdapter);
mBottomRv.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("tag", "down");
mHandler.removeCallbacks(scrollRunnable);
break;
case MotionEvent.ACTION_MOVE:
Log.e("tag", "move");
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem >= 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
break;
case MotionEvent.ACTION_UP:
Log.e("tag", "up");
mHandler.postDelayed(scrollRunnable, 10);
break;
}
return false;
}
});
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(scrollRunnable, 10);
}
@Override
protected void onStop() {
super.onStop();
mHandler.removeCallbacks(scrollRunnable);
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_display"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="fitXY"
android:src="@mipmap/ic_launcher_round"
app:layout_constraintBottom_toTopOf="@+id/rv_bottom_screen"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_bottom_screen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_display" />
</android.support.constraint.ConstraintLayout>
业务版
相比基础版,此版本功能在屏保效果中更常见一些,主要体现在用户触碰屏保页即可执行相关操作
功能:
- 支持底部商品无限横向滚动
- 支持滚动期间动态更换大图
不支持用户手动滑动底部商品图操作,取而代之的是只要用户触摸屏幕即执行相关操作
功能方面具备基础版中的所有功能,但因onTouch机制问题,在上层事件已经实现了拦截,体现在用户只要触碰屏幕就会执行相关操作,所以针对于RevyclerView的onTouch监听意义不大 ~
实现方面项目版和基础版大部分是相同的,不同点主要在于重写dispatchTouchEvent 事件,记得RecycleView的onTouch内别直接消耗掉事件~
实现过程
MainActivity
package nk.com.carouseldemo;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ImageView mDisplay;
RecyclerView mBottomRv;
//此处数据源为项目内的图片,不可套用
private Integer[] mImgIds = {
R.mipmap.banner_1, R.mipmap.banner_2, R.mipmap.banner_3};
private List<Integer> imgLists;
private ScreenAdapter screenAdapter;
private Handler mHandler = new Handler();
private LinearLayoutManager layoutManager;
private int oldItem = 0;
Runnable scrollRunnable = new Runnable() {
@Override
public void run() {
mBottomRv.scrollBy(1, 0);
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem > 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
mHandler.postDelayed(scrollRunnable, 10);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplay = findViewById(R.id.iv_display);
mBottomRv = findViewById(R.id.rv_bottom_screen);
initData();
}
public void initData() {
//模拟数据源
imgLists = new ArrayList<>();
for (int i = 0; i < mImgIds.length; i++) {
imgLists.add(mImgIds[i]);
}
//小优化:正常开发中一般为了用户体验初始的时候先手动加载第一个视图
mDisplay.setImageResource(imgLists.get(0));
screenAdapter = new ScreenAdapter(this, imgLists);
layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mBottomRv.setLayoutManager(layoutManager);
mBottomRv.setAdapter(screenAdapter);
mBottomRv.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("tag", "down");
mHandler.removeCallbacks(scrollRunnable);
break;
case MotionEvent.ACTION_MOVE:
Log.e("tag", "move");
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem >= 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
break;
case MotionEvent.ACTION_UP:
Log.e("tag", "up");
mHandler.postDelayed(scrollRunnable, 10);
break;
}
return false;
}
});
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 一般操作习惯是用户点击屏保即跳转首页 ~ 具体看个人需求
// Intent intent = new Intent(this, MainActivity.class);
// intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
// startActivity(intent);
// onBackPressed();
Log.e("tag", "跳转首页");
return true;
}
return super.dispatchTouchEvent(ev);
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(scrollRunnable, 10);
}
@Override
protected void onStop() {
super.onStop();
mHandler.removeCallbacks(scrollRunnable);
}
}
全面版
基本兼容基础版与业务版,唯一不同在于将业务版中触摸屏幕就关闭屏保的功能移植到了大图的事件上(大图的事件自己写咯 ~),这样就可以适用更多的场景咯 ~
功能:
- 支持底部商品无限横向滚动
- 支持滚动期间动态更换大图
- 支持用户手动滑动底部商品图操作
- 支持用户底部商品的点击事件
ScreenAdapter
package nk.com.carouseldemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.List;
/**
* @author MrLiu
* @date 2020/12/10
* desc
*/
public class ScreenAdapter extends RecyclerView.Adapter<ScreenAdapter.ViewHolder> {
private Context context;
private List<Integer> mDataList;
private OnItemClickListener onItemClickListener;
public ScreenAdapter(Context context, List<Integer> dataList) {
this.context = context;
this.mDataList = dataList;
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_screen, parent, false);
final ViewHolder vh = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(vh.getPosition());
}
});
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
int newPos = position % mDataList.size();
holder.mDisplay.setImageResource(mDataList.get(newPos));
holder.itemView.setTag(position);
}
class ViewHolder extends RecyclerView.ViewHolder {
ImageView mDisplay;
public ViewHolder(View itemView) {
super(itemView);
mDisplay = itemView.findViewById(R.id.iv_display);
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.onItemClickListener = listener;
}
interface OnItemClickListener {
void onItemClick(int position);
}
}
MainActivity
package nk.com.carouseldemo;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ImageView mDisplay;
RecyclerView mBottomRv;
private Integer[] mImgIds = {
R.mipmap.banner_1, R.mipmap.banner_2, R.mipmap.banner_3};
private List<Integer> imgLists;
private ScreenAdapter screenAdapter;
private Handler mHandler = new Handler();
private LinearLayoutManager layoutManager;
private int oldItem = 0;
Runnable scrollRunnable = new Runnable() {
@Override
public void run() {
mBottomRv.scrollBy(1, 0);
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem > 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
mHandler.postDelayed(scrollRunnable, 10);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDisplay = findViewById(R.id.iv_display);
mBottomRv = findViewById(R.id.rv_bottom_screen);
initData();
screenAdapter.setOnItemClickListener(new ScreenAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Log.e("tag", "position=" + position);
//因为使用的是MAX_VALUE,所以要获得真实position要记得 取% ~
Log.e("tag", "position=" + position % imgLists.size());
}
});
}
public void initData() {
//模拟数据源
imgLists = new ArrayList<>();
for (int i = 0; i < mImgIds.length; i++) {
imgLists.add(mImgIds[i]);
}
//小优化:正常开发中一般为了用户体验初始的时候先手动加载第一个视图
mDisplay.setImageResource(imgLists.get(0));
screenAdapter = new ScreenAdapter(this, imgLists);
layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mBottomRv.setLayoutManager(layoutManager);
mBottomRv.setAdapter(screenAdapter);
mBottomRv.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("tag", "down");
break;
case MotionEvent.ACTION_MOVE:
Log.e("tag", "move");
mHandler.removeCallbacks(scrollRunnable);
int firstItem = layoutManager.findFirstVisibleItemPosition();
if (firstItem != oldItem && firstItem >= 0) {
oldItem = firstItem;
mDisplay.setImageResource(imgLists.get(oldItem % imgLists.size()));
}
break;
case MotionEvent.ACTION_UP:
Log.e("tag", "up");
mHandler.postDelayed(scrollRunnable, 10);
break;
}
return false;
}
});
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(scrollRunnable, 10);
}
@Override
protected void onStop() {
super.onStop();
mHandler.removeCallbacks(scrollRunnable);
}
}