欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义HorizontalScrollView实现QQ消息列表效果

程序员文章站 2022-05-15 18:28:31
...

前言

以前一直用DrawerLayout实现侧滑栏,如今实在受不了DrawerLayout只能通过那最边缘20dp来侧拉出来,这里简单对比一下用DrawerLayout和自定义HorizontalScrollView的区别:

DrawerLayout:
优点:实现简单,只需简单通过DrawerLayout的滑动监听事件来
缺点:只能通过最边缘的20dp来触发滑动

自定义HorizontalScrollView:
优点:整个Item都可以触发滑动(实现QQ消息列表一样的效果)
缺点:实现略麻烦、并且需要给item写点击事件(可以不做事件,但是需要把焦点抢过来),不然的话会出现侧滑栏不在还可以点击得到的情况

效果图

自定义HorizontalScrollView实现QQ消息列表效果

自定义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);