自定义HorizontalScrollView实现QQ消息列表效果
前言
以前一直用DrawerLayout实现侧滑栏,如今实在受不了DrawerLayout只能通过那最边缘20dp来侧拉出来,这里简单对比一下用DrawerLayout和自定义HorizontalScrollView的区别:
DrawerLayout:
优点:实现简单,只需简单通过DrawerLayout的滑动监听事件来
缺点:只能通过最边缘的20dp来触发滑动
自定义HorizontalScrollView:
优点:整个Item都可以触发滑动(实现QQ消息列表一样的效果)
缺点:实现略麻烦、并且需要给item写点击事件(可以不做事件,但是需要把焦点抢过来),不然的话会出现侧滑栏不在还可以点击得到的情况
效果图
自定义View
实现原理
这里是自定义HorizontalScrollView,主要是利用HorizontalScrollView整体可以横向滑动的自带特性
通过onScrollChanged
根据拖动的距离来控制侧滑栏出来的距离
通过onTouchEvent
来实现两个监听,并且实现:当停止操作时,判断侧滑栏的状态,当侧滑栏被拉出的距离超过侧滑栏一半时停止操作就把整个侧滑栏弹出,当侧滑栏被拉出的距离低于侧滑栏一般时停止操作则把侧滑栏重新隐藏。
SideBarScrollView.java
public class SideBarScrollView extends HorizontalScrollView {
private LinearLayout slide;//侧滑栏
private int slideWidth;//侧滑最大限度
private SideBarScrollView.onMovingListener onMovingListener;//监听器
public SideBarScrollView(Context context) {
super(context, null);
}
public SideBarScrollView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public SideBarScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOverScrollMode(OVER_SCROLL_NEVER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
slide = findViewById(R.id.ll_slide);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//获取侧滑栏的宽度,即滑动的范围
slideWidth = slide.getWidth();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//根据拖动改变侧滑栏显示的宽度
slide.setTranslationX(l - slideWidth);
}
//定义一个监听
public interface onMovingListener {
void onMove(SideBarScrollView sideBarScrollView);
void onOpen(SideBarScrollView sideBarScrollView);
}
//定义监听
public void setListener(onMovingListener onMovingListener) {
this.onMovingListener = onMovingListener;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//正在操作时
onMovingListener.onMove(this);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//停止操作时
//判断拖动的距离有没有超过侧滑栏的一半(超过则整个弹出,否则关闭)
if (getScrollX() > slideWidth / 2) {
this.smoothScrollTo(slideWidth, 0);
onMovingListener.onOpen(this);
} else {
this.smoothScrollTo(0, 0);
}
return true;//停止操作时返回true
}
return super.onTouchEvent(ev);
}
}
结合RecyclerView使用
item
xml布局文件
这里的com.yjx.mystudylife.Features.SideBar.SideBarScrollView
是自定义的View
通过RelativeLayout的属性android:layout_toRightOf=""
来控制侧滑栏一直处于正常内容的右边
!!!注意:这里的左侧内容的LinearLayout必须给background,不然会出现侧滑栏一直存在的问题
<?xml version="1.0" encoding="utf-8"?>
<com.yjx.mystudylife.Features.SideBar.SideBarScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll_slide"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/ll_left">
<TextView
android:id="@+id/txv_top"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#F5DF1D"
android:gravity="center"
android:text="置顶" />
<TextView
android:id="@+id/txv_del"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#ff00"
android:gravity="center"
android:text="删除" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
<TextView
android:id="@+id/txv_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="18sp" />
</LinearLayout>
</RelativeLayout>
</com.yjx.mystudylife.Features.SideBar.SideBarScrollView>
RecyclerView的Adapter
这里直接实现在自定义View里面写的接口,然后通过两个接口实现:onOpen
:当侧滑栏打开时,把打开的Item赋值onMove
:移动监听,当有Item移动时,判断当前移动的Item和已经打开的Item是否一致,如果不一致,则关闭之前的侧滑栏
itemView需要加上监听:((SideBarScrollView) itemView).setListener(AdapterSideBar.this);
这里需要先强转
holder.llLeft.getLayoutParams().width = activity.getWindowManager().getDefaultDisplay().getWidth();
这里需要设置正常显示内容的宽度
public class AdapterSideBar extends RecyclerView.Adapter<AdapterSideBar.ViewHolder> implements SideBarScrollView.onMovingListener {
Activity activity;
List<String> list;
private SideBarScrollView scrollView;
public AdapterSideBar(Activity activity, List<String> list) {
this.activity = activity;
this.list = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sidebar_item, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.llLeft.getLayoutParams().width = activity.getWindowManager().getDefaultDisplay().getWidth();
holder.txvName.setText(list.get(position));
//左边正常内容需要有监听事件,抢夺焦点
holder.llLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
holder.txvTop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "置顶", Toast.LENGTH_SHORT).show();
}
});
holder.txvDel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "删除", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return list.size();
}
@Override
public void onMove(SideBarScrollView sideBarScrollView) {
if (scrollView != null && scrollView != sideBarScrollView) {
scrollView.smoothScrollTo(0, 0);
scrollView = null;
}
}
@Override
public void onOpen(SideBarScrollView sideBarScrollView) {
scrollView = sideBarScrollView;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView txvTop;
private TextView txvDel;
private LinearLayout llLeft;
private TextView txvName;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txvTop = (TextView) itemView.findViewById(R.id.txv_top);
txvDel = (TextView) itemView.findViewById(R.id.txv_del);
llLeft = (LinearLayout) itemView.findViewById(R.id.ll_left);
txvName = (TextView) itemView.findViewById(R.id.txv_name);
((SideBarScrollView) itemView).setListener(AdapterSideBar.this);
}
}
}
主要要实现的功能都已经贴完了,但是作为一个有始有终的男人,接来下Activity里面就是正常的RecyclerView的实现了
RecyclerViewActivity
这里就是简单的实现RecyclerView
for (int i = 0; i < 5; i++) {
list.add(i + "");
}
adapterSideBar = new AdapterSideBar(this, list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(adapterSideBar);
推荐阅读
-
android自定义view之模拟qq消息拖拽删除效果
-
Android自定义HorizontalScrollView实现qq侧滑菜单
-
Android开发之自定义view实现通讯录列表A~Z字母提示效果【附demo源码下载】
-
android自定义view之模拟qq消息拖拽删除效果
-
(仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画
-
Android 自定义View系列之贝塞尔曲线+QQ未读消息拖拽效果实现+水波浪充电效果
-
用Qt实现QQ好友列表界面伸缩功能(完全一模一样)(伸展和收缩、抽屉效果、类似树形控件)(鼠标划过QSS效果)
-
今天分享一个 HorizontalScrollView整体滑块,实现列表效果
-
iOS之仿QQ好友列表展开收缩效果的实现
-
Android自定义HorizontalScrollView实现qq侧滑菜单