仿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
- 仿qq列表,分类悬停
- Android UI设计: 仿QQ好友列表分组悬停,自定义Header,下拉刷新结合Demo
- 干货,仿qq列表,手把手实现分类悬浮提示
- Android仿QQ联系人分组悬停
- 仿qq列表
- 仿QQ下拉列表
- 仿QQ好友列表
- 仿QQ联系人列表
- android仿QQ列表实现
- ExpandableListView仿QQ好友列表
- ExpandableListView仿QQ好友列表
- 仿qq向左滑动列表
- 仿QQ好友列表 ExpandableListView
- Android_ExpandableListView_仿QQ好友列表
- c#仿qq好友列表控件
- 完全仿QQ好友列表,自定义ExpandableListView!
- 完全仿QQ好友列表,自定义ExpandableListView!
- android仿QQ列表的效果实现
- C#几个经常用到的字符串截取
- AIDL实现跨APP通信(双向通信)
- 2016.09.08回顾
- TEST
- 优秀程序员的十个习惯
- 仿qq列表,分类悬停
- java调用rest风格web服务的两种方式
- thinkPHP PHPMailer 发送邮件
- 2-5 Linux编程规范
- linux上如何查看当前有多少人使用服务器的vpn或ssh服务?
- jq实现简单的tab切换效果
- 结构性伪类选择器—empty
- [3]数据库多表连接查询
- TCP、UDP、HTTP、SOCKET之间的区别