仿qq列表,分类悬停

来源:互联网 发布:vscode vue 开发环境 编辑:程序博客网 时间:2024/05/21 10:38

一直在找寻分类悬停,网上虽有很多,但还是要自己保存一份。
这里要声明感谢甄某某某某,是看到此人博客学习借鉴的同时,自己保留,毕竟以后找起来也好找,不用漫天去搜索了。
附上链接:[http://www.jianshu.com/p/a52936d2dcd9]
先上效果图:
这里写图片描述
实现原理就是自定义ExpandableListView,给它设置一个指示布局,在滑动过程中监听当前是否应该悬浮显示分类来实现的。
下面上代码:
首先来看我们需要自定义的一个类:
CustomExpandListview,它是继承ExpandableListView来实现我们的效果:

import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.ExpandableListAdapter;import android.widget.ExpandableListView;public class CustomExpandListview extends ExpandableListView implements        AbsListView.OnScrollListener, ExpandableListView.OnGroupClickListener {    public CustomExpandListview(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        registerListener();    }    public CustomExpandListview(Context context, AttributeSet attrs) {        super(context, attrs);        registerListener();    }    public CustomExpandListview(Context context) {        super(context);        registerListener();    }    /**     * Adapter 接口 . 列表必须实现此接口 .     */    public interface HeaderAdapter {        public static final int PINNED_HEADER_GONE = 0;        public static final int PINNED_HEADER_VISIBLE = 1;        public static final int PINNED_HEADER_PUSHED_UP = 2;        /**         * 获取 Header 的状态         *         * @param groupPosition         * @param childPosition         * @return PINNED_HEADER_GONE, PINNED_HEADER_VISIBLE, PINNED_HEADER_PUSHED_UP         * 其中之一         */        int getHeaderState(int groupPosition, int childPosition);        /**         * @param header         * @param groupPosition         * @param childPosition         * @param alpha         */        void configureHeader(View header, int groupPosition,                             int childPosition, int alpha);    }    private static final int MAX_ALPHA = 255;    private HeaderAdapter mAdapter;    /**     * 用于在列表头显示的 View,mHeaderViewVisible 为 true 才可见     */    private View mHeaderView;    /**     * 列表头是否可见     */    private boolean mHeaderViewVisible;    private int mHeaderViewWidth;    private int mHeaderViewHeight;    public void setHeaderView(View view) {        mHeaderView = view;        AbsListView.LayoutParams lp = new AbsListView.LayoutParams(                ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.WRAP_CONTENT);        view.setLayoutParams(lp);        if (mHeaderView != null) {            setFadingEdgeLength(0);        }        requestLayout();    }    private void registerListener() {        setOnScrollListener(this);        setOnGroupClickListener(this);    }    /**     * 点击 HeaderView 触发的事件     */    private void headerViewClick() {        long packedPosition = getExpandableListPosition(this                .getFirstVisiblePosition());        int groupPosition = ExpandableListView                .getPackedPositionGroup(packedPosition);        this.collapseGroup(groupPosition);        this.setSelectedGroup(groupPosition);    }    private float mDownX;    private float mDownY;    /**     * 如果 HeaderView 是可见的 , 此函数用于判断是否点击了 HeaderView, 并对做相应的处理 , 因为 HeaderView     * 是画上去的 , 所以设置事件监听是无效的 , 只有自行控制 .     */    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (mHeaderViewVisible) {            switch (ev.getAction()) {                case MotionEvent.ACTION_DOWN:                    mDownX = ev.getX();                    mDownY = ev.getY();                    if (mDownX <= mHeaderViewWidth && mDownY <= mHeaderViewHeight) {                        return true;                    }                    break;                case MotionEvent.ACTION_UP:                    float x = ev.getX();                    float y = ev.getY();                    float offsetX = Math.abs(x - mDownX);                    float offsetY = Math.abs(y - mDownY);                    // 如果 HeaderView 是可见的 , 点击在 HeaderView 内 , 那么触发 headerClick()                    if (x <= mHeaderViewWidth && y <= mHeaderViewHeight                            && offsetX <= mHeaderViewWidth                            && offsetY <= mHeaderViewHeight) {                        if (mHeaderView != null) {                            headerViewClick();                        }                        return true;                    }                    break;                default:                    break;            }        }        return super.onTouchEvent(ev);    }    @Override    public void setAdapter(ExpandableListAdapter adapter) {        super.setAdapter(adapter);        mAdapter = (HeaderAdapter) adapter;    }    /**     * 点击了 Group 触发的事件 , 要根据根据当前点击 Group 的状态来     */    @Override    public boolean onGroupClick(ExpandableListView parent, View v,                                int groupPosition, long id) {        if (isGroupExpanded(groupPosition)) {            parent.collapseGroup(groupPosition);        } else {            parent.expandGroup(groupPosition);        }        return true;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (mHeaderView != null) {            measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);            mHeaderViewWidth = mHeaderView.getMeasuredWidth();            mHeaderViewHeight = mHeaderView.getMeasuredHeight();        }    }    private int mOldState = -1;    @Override    protected void onLayout(boolean changed, int left, int top, int right,                            int bottom) {        super.onLayout(changed, left, top, right, bottom);        final long flatPostion = getExpandableListPosition(getFirstVisiblePosition());        final int groupPos = ExpandableListView                .getPackedPositionGroup(flatPostion);        final int childPos = ExpandableListView                .getPackedPositionChild(flatPostion);        int state = mAdapter.getHeaderState(groupPos, childPos);        if (mHeaderView != null && mAdapter != null && state != mOldState) {            mOldState = state;            mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);        }        configureHeaderView(groupPos, childPos);    }    public void configureHeaderView(int groupPosition, int childPosition) {        if (mHeaderView == null || mAdapter == null                || ((ExpandableListAdapter) mAdapter).getGroupCount() == 0) {            return;        }        int state = mAdapter.getHeaderState(groupPosition, childPosition);        switch (state) {            case HeaderAdapter.PINNED_HEADER_GONE: {                mHeaderViewVisible = false;                break;            }            case HeaderAdapter.PINNED_HEADER_VISIBLE: {                mAdapter.configureHeader(mHeaderView, groupPosition,                        childPosition, MAX_ALPHA);                if (mHeaderView.getTop() != 0) {                    mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);                }                mHeaderViewVisible = true;                break;            }            case HeaderAdapter.PINNED_HEADER_PUSHED_UP: {                View firstView = getChildAt(0);                int bottom = firstView.getBottom();                // intitemHeight = firstView.getHeight();                int headerHeight = mHeaderView.getHeight();                int y;                int alpha;                if (bottom < headerHeight) {                    y = (bottom - headerHeight);                    alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;                } else {                    y = 0;                    alpha = MAX_ALPHA;                }                mAdapter.configureHeader(mHeaderView, groupPosition,                        childPosition, alpha);                if (mHeaderView.getTop() != y) {                    mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight                            + y);                }                mHeaderViewVisible = true;                break;            }        }    }    @Override    /**     * 列表界面更新时调用该方法(如滚动时)     */    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mHeaderViewVisible) {            // 分组栏是直接绘制到界面中,而不是加入到ViewGroup中            drawChild(canvas, mHeaderView, getDrawingTime());        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,                         int visibleItemCount, int totalItemCount) {        final long flatPos = getExpandableListPosition(firstVisibleItem);        int groupPosition = ExpandableListView.getPackedPositionGroup(flatPos);        int childPosition = ExpandableListView.getPackedPositionChild(flatPos);        configureHeaderView(groupPosition, childPosition);    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {    }}

接着MyAdapter

import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseExpandableListAdapter;import android.widget.TextView;import java.util.ArrayList;import java.util.HashMap;import java.util.Map;public class MyAdapter extends BaseExpandableListAdapter implements CustomExpandListview.HeaderAdapter {    private ArrayList<String> parent;//    private Map<String, ArrayList<String>> datas = new HashMap<>();    private Map<String, ArrayList<String>> datas = new HashMap<String, ArrayList<String>>();    private Context context;    private CustomExpandListview listview;    public MyAdapter(Context context, ArrayList<String> parent, Map<String, ArrayList<String>> datas, CustomExpandListview listview) {        this.context = context;        this.parent = parent;        this.datas = datas;        this.listview = listview;    }    @Override    public int getGroupCount() {        int temp = 0;        if (parent != null) {            temp = parent.size();        }        return temp;    }    @Override    public int getChildrenCount(int i) {        String key = parent.get(i);        int size = datas.get(key).size();        return size;    }    @Override    public Object getGroup(int i) {        return parent.get(i);    }    @Override    public Object getChild(int i, int i1) {        String key = parent.get(i);        return (datas.get(key).get(i1));    }    @Override    public long getGroupId(int i) {        return i;    }    @Override    public long getChildId(int i, int i1) {        return i1;    }    @Override    public boolean hasStableIds() {        return true;    }    @Override    public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {        if (view == null) {            LayoutInflater inflater = (LayoutInflater) context                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);            view = inflater.inflate(R.layout.parent_layout, null);        }        TextView tv = (TextView) view                .findViewById(R.id.tv_parent);        tv.setText(parent.get(i));        return view;    }    @Override    public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {        PlaceHolder holder;        if (view == null) {            holder = new PlaceHolder();            view = LayoutInflater.from(context).inflate(R.layout.item_layout, null);            holder.tv_title = (TextView) view.findViewById(R.id.tv_item);            view.setTag(holder);        } else {            holder = (PlaceHolder) view.getTag();        }        String key = parent.get(i);        String s = datas.get(key).get(i1);        holder.tv_title.setText(s);        return view;    }    @Override    public boolean isChildSelectable(int i, int i1) {        return true;    }    /**     *根据当前的groupPosition和childPosition判断指示布局是哪种状态(隐藏、可见、正在向上推)     * @param groupPosition     * @param childPosition     * @return     */    @Override    public int getHeaderState(int groupPosition, int childPosition) {        final int childCount = getChildrenCount(groupPosition);        if (childPosition == childCount - 1) {            return PINNED_HEADER_PUSHED_UP;        } else if (childPosition == -1                && !listview.isGroupExpanded(groupPosition)) {            return PINNED_HEADER_GONE;        } else {            return PINNED_HEADER_VISIBLE;        }    }    /**     * 给指示布局设置内容     * @param header     * @param groupPosition     * @param childPosition     * @param alpha     */    @Override    public void configureHeader(View header, int groupPosition, int childPosition, int alpha) {        if (groupPosition > -1) {            ((TextView) header.findViewById(R.id.tv_indictor))                    .setText(parent.get(groupPosition));        }    }    /**     * 子布局     */    private class PlaceHolder {        private TextView tv_title;    }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    >    <com.example.expandablelistviewdemo.CustomExpandListview        android:id="@+id/listView"        android:layout_width="match_parent"        android:layout_height="match_parent"></com.example.expandablelistviewdemo.CustomExpandListview></RelativeLayout>

item_layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <TextView        android:id="@+id/tv_item"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="ss"        android:textSize="20sp" /></LinearLayout>

indictor_layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#ff0000"    android:orientation="vertical">    <TextView        android:id="@+id/tv_indictor"        android:textSize="26sp"        android:text="ss"        android:layout_width="match_parent"        android:layout_height="wrap_content" /></LinearLayout>

parent_layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#ff0000"    android:orientation="vertical">    <TextView        android:id="@+id/tv_parent"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="ss"        android:textSize="26sp" /></LinearLayout>

那么怎么具体的使用呢?
看MainActivity

import java.util.ArrayList;import java.util.HashMap;import java.util.Map;import android.os.Bundle;import android.app.Activity;import android.view.Menu;import android.view.View;import android.widget.ExpandableListView;import android.widget.Toast;public class MainActivity extends Activity {    private CustomExpandListview listview;    private MyAdapter myAdapter;    private String[] parentSource = {"分类1", "分类2", "分类3", "分类4", "分类5"};    private ArrayList<String> parent = new ArrayList<String>();    private Map<String, ArrayList<String>> datas = new HashMap<String, ArrayList<String>>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);         listview = (CustomExpandListview) findViewById(R.id.listView);         //模拟数据            for (int i = 0; i < parentSource.length; i++) {                parent.add(parentSource[i]);            }            for (int i = 0; i < parent.size(); i++) {                String str = parent.get(i);                ArrayList<String> temp = new ArrayList<String>();                for (int j = 0; j < 20; j++) {                    temp.add("" + j);                }                datas.put(str, temp);            }            myAdapter = new MyAdapter(this, parent, datas, listview);            listview.setAdapter(myAdapter);            listview.setHeaderView(getLayoutInflater().inflate(                    R.layout.indictor_layout, listview, false));            listview.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {                @Override                public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {                    Toast.makeText(MainActivity.this, "点击了第" + (i + 1) + " 类的第" + i1 + "项", Toast.LENGTH_SHORT).show();                    return true;                }            });            // 默认全部展开            for (int i = 0; i < parent.size(); i++) {                listview.expandGroup(i);            }        }}

好了这样一个分类悬停的功能就实现了,代码有注释而且很清楚,肯定要比我写的清晰,所以就不做过多解释了,也因为本人比较懒,大家见谅了哈,有需要的朋友拿去用,大家可以叫我代码搬运工。

1 0
原创粉丝点击