精华内容
下载资源
问答
  • Google原生Launcher是用抽屉来装所有App的,但是抽屉真方便吗?最近市场上流行好多轻量级桌面都纷纷抛弃了抽屉,用Apps索引来代替。 如图: 我们来分析一下然后自己一个 1.整体可以重写RelativeLayout ...

    本文介绍Launcher中的Apps字母索引功能,什么是字母索引呢?

    Google的原生的Launcher是用抽屉来装所有App的,但是抽屉真的方便吗?最近市场上流行的好多轻量级桌面都纷纷抛弃了抽屉,用Apps索引来代替。

    如图:


    我们来分析一下然后自己做一个

    1.整体可以重写RelativeLayout

    2.右边的滑动的字母条可以重写LinearLayout

    3.中间装App的用ListView,并重写一个Item

    4.Item重写LinearLayout

    如下:


    1.AppsCustomsizeIndexView.java  整体组件

    初始化Listiew、重写BaseAdapter

    初始化自定义的AppsIndexLayout 、初始化时获取所有APP的title字段,生成字母List,里面不能有重复,然后排序,准备在AppsIndexLayout中使用。

    XML:

    <?xml version="1.0" encoding="utf-8"?>
    <com.ola.launcher.AppsCustomizeIndexView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:launcher="http://schemas.android.com/apk/res/com.ola.launcher"
        android:id="@+id/apps_customize_indexview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="visible"
        launcher:itemIconHeight="50dp"
        launcher:itemIconWidth="50dp" >//这两个属性暂时没有用,可以不加
    
        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:textSize="50dp" />
    
        <ListView
            android:id="@+id/apps_customize_listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/textview"
            android:layout_toLeftOf="@+id/apps_customize_indexlayout"
            android:background="@null"
            android:divider="@null"
            android:scrollbars="none" >//中间装app的ListView
        </ListView>
    
        <com.ola.launcher.AppsIndexLayout
            android:id="@+id/apps_customize_indexlayout"
            android:layout_width="@dimen/apps_custom_indexview_width"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_below="@+id/textview"
            android:layout_marginBottom="@dimen/apps_custom_indexview_marginbottom"
            android:background="@android:color/holo_blue_dark" >
        </com.ola.launcher.AppsIndexLayout>//右边的字母条
    
    </com.ola.launcher.AppsCustomizeIndexView>

    代码:

    public class AppsCustomizeIndexView extends RelativeLayout implements ScrollListener {
    
    	private TextView mTextView;
    	private AppsIndexLayout mIndexLayout;//字母条
    	private ListView mListView;
    	private AppsAdapter mAppsAdapter;
    	private ArrayList<String> mKeys = new ArrayList<String>();//字母条需要的数据
    	private HashMap<String, ArrayList<ApplicationInfo>> mKeyItem = new HashMap<String, ArrayList<ApplicationInfo>>();//app数据
    	private ArrayList<ApplicationInfo> mIndexApps = new ArrayList<ApplicationInfo>();//零时装app的List
    	private ArrayList<ApplicationInfo> mApplicationInfos;
    	String mEmpty = "";
    	String mRencent = "⊙";
    	String mOther = "#";
    
    	public AppsCustomizeIndexView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    	}
    
    	public AppsCustomizeIndexView(Context context) {
    		super(context);
    	}
    
    	protected void onFinishInflate() {
    		// TODO Auto-generated method stub
    		super.onFinishInflate();
    		mIndexLayout = (AppsIndexLayout) findViewById(R.id.apps_customize_indexlayout);
    		mListView = (ListView) findViewById(R.id.apps_customize_listview);
    		mIndexLayout.setOnScrollListener(this);
    		mTextView = (TextView) findViewById(R.id.textview);
    
    	}
    
    	@SuppressLint("ClickableViewAccessibility")
    	public boolean onTouchEvent(MotionEvent event) {
    		return true;
    	}
    
    	private void setTestText(String s) {
    		mTextView.setText(s);
    	}
    
    	public void setDate(ArrayList<ApplicationInfo> infos) {
    		this.mApplicationInfos = infos;
    		setTestText(mApplicationInfos.size() + "");
    		mKeys = getKeyFromApps();
    		mIndexLayout.setDate(mKeys);
    		mAppsAdapter = new AppsAdapter(mKeys, mKeyItem, getContext());
    		mListView.setAdapter(mAppsAdapter);
    
    	}
    
    	@SuppressLint("DefaultLocale")//通过遍历所有AppsList的App的title字段,取出第一个字符,生成一个List
    	private ArrayList<String> getKeyFromApps() {
    		ArrayList<String> mIndexKey = new ArrayList<String>();
    		String language = Locale.getDefault().getLanguage();
    		Pattern pattern = Pattern.compile("^[A-Za-z]+$");
    		for (int i = 0; i < mApplicationInfos.size(); i++) {
    			String key = mApplicationInfos.get(i).title.toString().trim().toUpperCase().charAt(0) + mEmpty;
    			if (pattern.matcher(key).matches()) {//匹配大小写英文字母
    				if (!mIndexKey.contains(key)) {
    					mIndexKey.add(key);
    					mIndexApps = new ArrayList<ApplicationInfo>();
    					mKeyItem.put(key, mIndexApps);
    				} else {
    					mIndexApps = mKeyItem.get(key);
    				}
    				if (mIndexApps != null) {
    					mIndexApps.add(mApplicationInfos.get(i));
    				}
    			} else {
    				if (Locale.CHINA.getLanguage().equalsIgnoreCase(language)) {
    					char c = key.toCharArray()[0];
    					if (Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS.equals(Character.UnicodeBlock.of(c))) {
    	                                        //中文时候的处理、还有其他语言考虑
    
    					} else {
    						addOther(mIndexKey, mKeyItem, mApplicationInfos.get(i), mOther);//乱码时候处理
    					}
    				} else {
    					String o = key;
    					if (!mIndexKey.contains(o)) {
    						mIndexKey.add(o);
    						mIndexApps = new ArrayList<ApplicationInfo>();
    						mKeyItem.put(o, mIndexApps);
    					} else {
    						mIndexApps = mKeyItem.get(o);
    					}
    					if (mIndexApps != null) {
    						mIndexApps.add(mApplicationInfos.get(i));
    					}
    				}
    			}
    		}
    		// mIndexKey.add(mRencent);
    		// mIndexKey.add(mOther);
    		Collections.sort(mIndexKey);
    		addOther(mIndexKey, mKeyItem, mApplicationInfos.get(0), mOther);
    		addOther(mIndexKey, mKeyItem, mApplicationInfos.get(0), mRencent);
    		return mIndexKey;
    	}
    
    	@Override
    	public void onDrag(String key) {
    		mListView.setSelection(findPosFromList(key));
    	}
    
    	private void addOther(ArrayList<String> keystrings, HashMap<String, ArrayList<ApplicationInfo>> keyitem,
    			ApplicationInfo apps, String string) {
    		ArrayList<ApplicationInfo> mInfos;
    		if (!keystrings.contains(string)) {
    			keystrings.add(0, string);
    			mInfos = new ArrayList<ApplicationInfo>();
    			keyitem.put(string, mInfos);
    		} else {
    			mInfos = keyitem.get(string);
    
    		}
    		if (mInfos != null) {
    			mInfos.add(apps);
    		}
    
    	}
    
    	public int findPosFromList(String key) {
    		for (int i = 0; i < mKeys.size(); i++) {
    			if (key.equals(mKeys.get(i))) {
    				return i;
    			}
    		}
    		return 0;
    	}
    
    	class AppsAdapter extends BaseAdapter {
    		private ArrayList<String> stringsArray;
    		private HashMap<String, ArrayList<ApplicationInfo>> mKeyItem;
    		Context context;
    
    		public AppsAdapter(ArrayList<String> stringsArray, HashMap<String, ArrayList<ApplicationInfo>> mKeyItem, Context context) {
    			this.stringsArray = stringsArray;
    			this.context = context;
    			this.mKeyItem = mKeyItem;
    		}
    
    		@Override
    		public int getCount() {
    			return stringsArray.size();
    		}
    
    		@Override
    		public Object getItem(int position) {
    			return null;
    		}
    
    		@Override
    		public long getItemId(int position) {
    			return 0;
    		}
    
    		@SuppressLint("InflateParams")
    		@Override
    		public View getView(int position, View convertView, ViewGroup parent) {
    
    			if (convertView == null) {
    				convertView = LayoutInflater.from(context).inflate(R.layout.apps_customize_indexview_item, null);
    			}
    			((AppsCustomizeIndexViewItemLayout) convertView).setKeyString(stringsArray.get(position).toString());
    			((AppsCustomizeIndexViewItemLayout) convertView).setMeasure(mKeyItem.get(stringsArray.get(position)).size());
    			((AppsCustomizeIndexViewItemLayout) convertView).setContent(mKeyItem.get(stringsArray.get(position)));
    			return convertView;
    		}
    
    	}
    
    }

    2.AppsIndexLayout.java 右边的字母条组件

    我们来分析一下:

    仅仅是显示一排字母的话,我们就让他继承LinearLayout,并且设置排列方式为竖直,然后在LinearLayout中添加上若干个TextView就可以,用TextView来显示具体的字母。

    再自己定义一个接口ScrollListener,用来在触摸事件中去触发该接口的方法并传递参数,通过该方法去控制另外组件的状态变化

    触摸过程中调用接口的方法,并且计算位置,让触摸周围的TexView高亮显示

    java:

    public class AppsIndexLayout extends LinearLayout {
    
    	public ScrollListener mDragListener;
    	private ArrayList<String> mIndexKey;
    	private TextView mTextView;
    	private int mViewHeight;
    	private Rect mHitRect = new Rect();
    	private String mColor3 = "#B8B3AE";
    	private String mColor2 = "#C7C5BD";
    	private String mColor1 = "#FFFFFF";
    
    	public AppsIndexLayout(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		setOrientation(VERTICAL);
    		setGravity(Gravity.CENTER_HORIZONTAL);
    
    	}
    
    	// @Override
    	// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    	// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    	// int result_w = mearWidth(widthMeasureSpec);
    	// int result_h = mearHeight(heightMeasureSpec);
    	//
    	// setMeasuredDimension(result_w, result_h);
    	// }
    
    	// private int mearWidth(int widthMeasureSpec) {
    	// int result = 0;
    	// int specMode = MeasureSpec.getMode(widthMeasureSpec);
    	// int spacSize = MeasureSpec.getSize(widthMeasureSpec);
    	// if (specMode == MeasureSpec.EXACTLY) {
    	// result = spacSize;
    	// } else {
    	// result = getResources().getInteger(R.dimen.apps_custom_indexview_width);
    	// if (specMode == MeasureSpec.AT_MOST) {
    	// result = Math.min(result, spacSize);
    	// }
    	// }
    	// return result;
    	// }
    
    	// private int mearHeight(int heightMeasureSpec) {
    	// int result = 0;
    	// int specMode = MeasureSpec.getMode(heightMeasureSpec);
    	// int spacSize = MeasureSpec.getSize(heightMeasureSpec);
    	// if (specMode == MeasureSpec.EXACTLY) {
    	// result = spacSize;
    	// } else {
    	// result = getResources().getDisplayMetrics().heightPixels;
    	// if (specMode == MeasureSpec.AT_MOST) {
    	// result = Math.min(result, spacSize);
    	// }
    	// }
    	// mViewHeight = result;
    	// return result;
    	// }
    
    	public void setDate(ArrayList<String> indexkey) {
    		int h = getResources().getDisplayMetrics().heightPixels;
    		mIndexKey = indexkey;
    		LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, h / (indexkey.size() + 6));
    		for (int i = 0; i < mIndexKey.size(); i++) {
    			TextView textView = new TextView(getContext());
    			textView.setGravity(Gravity.CENTER);
    			textView.setTextSize(12);
    			textView.setText(mIndexKey.get(i).toString());
    			addView(textView, layoutParams);
    		}
    
    	}
    
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    		case MotionEvent.ACTION_UP:
    		case MotionEvent.ACTION_MOVE:
    		default:
    			getKeyString(event);
    			break;
    		}
    		return true;
    	}
    
    	private void getKeyString(MotionEvent event) {
    		int x = (int) event.getX();
    		int y = (int) event.getY();
    		for (int i = 0; i < getChildCount(); i++) {
    			if (getViewByEvent(x, y, getChildAt(i))) {
    				((TextView) getChildAt(i)).setTextColor(Color.parseColor(mColor1));
    				mDragListener.onDrag(((TextView) getChildAt(i)).getText().toString());//调用接口的方法,参数传递我们当前的TextView的文本
    				if (i + 1 < getChildCount()) {
    					((TextView) getChildAt(i + 1)).setTextColor(Color.parseColor(mColor1));//计算触摸时的TextView位置,在越界的情况下让其上下的都高亮显示
    					for (int j = i + 2; j < getChildCount(); j++) {
    						((TextView) getChildAt(j)).setTextColor(Color.parseColor(mColor2));
    					}
    				}
    				if (i - 1 > 0) {
    
    					((TextView) getChildAt(i - 1)).setTextColor(Color.parseColor(mColor1));
    					for (int j = 0; j < i - 2; j++) {
    						((TextView) getChildAt(j)).setTextColor(Color.parseColor(mColor2));
    
    					}
    				}
    
    			}
    		}
    	}
    
    	private boolean getViewByEvent(int x, int y, View v) {
    		v.getHitRect(mHitRect);
    		int i = (mHitRect.left + mHitRect.right) / 2;
    		return mHitRect.contains(i, y);
    	}
    
    	public void addChild(TextView view) {
    		addView(view);
    	}
    
    	public void setOnScrollListener(ScrollListener dragListener) {
    		this.mDragListener = dragListener;
    	}
    
    	interface ScrollListener {
    		public void onDrag(String key);
    	}
    }

    3.AppsCustomsizeIndexViewItemLayout.java 

    ListView中的高矮不一的Item,里面显示大字母的TextView,显示具体App的GridLayout

    GridLayout可以设置行列

    Xml:apps_customsize_indexview_item

    <?xml version="1.0" encoding="utf-8"?>
    <com.ola.launcher.AppsCustomizeIndexViewItemLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_orange_light" >
    
        <TextView
            android:id="@+id/apps_index_item_key"
            android:layout_width="@dimen/apps_custom_indexview_key_width"
            android:layout_height="@dimen/apps_custom_indexview_key_height"
            android:layout_marginLeft="@dimen/apps_custom_indexview_key_marginleft"
            android:layout_marginTop="@dimen/apps_custom_indexview_key_margintop"
            android:background="@drawable/apps_index_item_key_bg"
            android:textColor="@android:color/white"
            android:gravity="center"
            android:textSize="@dimen/apps_custom_indexview_key_textsize" />
    
        <!-- <GridLayout
            android:id="@+id/apps_index_item_grid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </GridLayout> -->
    
    </com.ola.launcher.AppsCustomizeIndexViewItemLayout>

    Java:

    public class AppsCustomizeIndexViewItemLayout extends LinearLayout implements OnClickListener {
    
    	private TextView mKeyView;
    	private GridLayout mAppsGridLayout;
    	private int mGridCellX = 4;
    	Launcher mLauncher;
    	private IconCache mIconCache;
    
    	public AppsCustomizeIndexViewItemLayout(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		mLauncher = (Launcher) context;
    		mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache();
    	}
    
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    	}
    
    	private int onMeasureWidth(int widthMeasureSpec) {
    		int result = 0;
    		int specMode = MeasureSpec.getMode(widthMeasureSpec);
    		int specSize = MeasureSpec.getSize(widthMeasureSpec);
    		if (specMode == MeasureSpec.EXACTLY) {
    			result = specSize;
    		} else {
    			result = specSize;
    		}
    		return result;
    	}
    
    	private int onMeasureHeight(int heightMeasureSpaec) {
    		int result = 0;
    		int specMode = MeasureSpec.getMode(heightMeasureSpaec);
    		int spacSize = MeasureSpec.getSize(heightMeasureSpaec);
    		if (specMode == MeasureSpec.EXACTLY) {
    			result = spacSize;
    		} else {
    			result = spacSize;
    		}
    		return result;
    	}
    
    	@Override
    	protected void onFinishInflate() {
    		super.onFinishInflate();
    		mKeyView = (TextView) findViewById(R.id.apps_index_item_key);
    		// mAppsGridLayout = (GridLayout)
    		// findViewById(R.id.apps_index_item_grid);
    		mAppsGridLayout = new GridLayout(getContext());
    		mAppsGridLayout.setColumnCount(mGridCellX);
    		addView(mAppsGridLayout);
    
    	}
    
    	public void setKeyString(String key) {
    		mKeyView.setText(key);
    	}
    
    	public void setGridBackground(int c) {
    		mAppsGridLayout.setBackgroundColor(c);
    	}
    
    	public void setMeasure(int count) {
    
    	}
    
    	public void setContent(ArrayList<ApplicationInfo> infos) {
    		mAppsGridLayout.removeAllViews();
    		IconTextView itemView;
    		for (int i = 0; i < infos.size(); i++) {
    			// itemView = (PagedViewIcon)
    			// LayoutInflater.from(getContext()).inflate(R.layout.apps_customize_application,
    			// null);
    			itemView = new IconTextView(getContext());
    			// itemView.applyFromApplicationInfo(infos.get(i), true, null);
    			itemView.setIcon(new FastBitmapDrawable(infos.get(i).iconBitmap));
    			itemView.setText(infos.get(i).title);
    			// itemView.setOnClickListener(this);
    			mAppsGridLayout.addView(itemView, Math.min(1, mAppsGridLayout.getChildCount()), new LayoutParams(140, 200));
    		}
    	}
    
    	@Override
    	public void onClick(View v) {
    		ApplicationInfo info = (ApplicationInfo) v.getTag();
    		startActivity(info, v);
    	}
    
    	public void startActivity(ApplicationInfo info, View v) {
    		final Intent intent = info.intent;
    		int[] pos = new int[2];
    		v.getLocationOnScreen(pos);
    		intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()));
    		if (!mLauncher.startActivitySafely(v, intent, null)) {
    
    		}
    	}
    
    	class IconTextView extends TextView {//显示AppItem View
    
    		public IconTextView(Context context) {
    			super(context);
    			setGravity(Gravity.CENTER_HORIZONTAL);
    			setPadding(10, 20, 10, 10);
    
    			// setTextColor(Color.BLACK);
    		}
    
    		public void setIcon(Drawable d) {
    			setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);//TextView上下左右设置图片的,这里我们只给top设置
    		}
    	}
    
    }

    最后们来看看功能的实现效果:




    展开全文
  • 这是个不会改变常量,每次过滤都是过滤这个数组 getData() { this.list = this.constMerchantData.map(item => { var children = item.children.filter( item2 => item2.merchant_name.indexOf(this.vague) > -1) ...

    话不多说,先上效果图

    在这里插入图片描述

    数据格式

    [
    			{
    				"name": "A",
    				"children": [{
    					"merchant_name": "阿三1",
    					"merchant_id": 1,
    					"name": "A"
    				}, {
    					"merchant_name": "阿四2",
    					"merchant_id": 2,
    					"name": "A"
    				}, {
    					"merchant_name": "A狗桦2",
    					"merchant_id": 22,
    					"name": "A"
    				}, {
    					"merchant_name": "A狗桦3",
    					"merchant_id": 23,
    					"name": "A"
    				}]
    			}, {
    				"name": "B",
    				"children": [{
    					"merchant_name": "B暑荣3",
    					"merchant_id": 3,
    					"name": "B"
    				}, {
    					"merchant_name": "B拓4",
    					"merchant_id": 4,
    					"name": "B"
    
    				}, {
    					"merchant_name": "B秀秀秀2",
    					"merchant_id": 222,
    					"name": "B"
    				}, {
    					"merchant_name": "B秀秀秀3",
    					"merchant_id": 233,
    					"name": "B"
    				}]
    			}
    ]
    

    主要过滤数据代码部分,this.constMerchantData这是一个所有商户的数组,这是个不会改变的常量,每次过滤都是过滤这个数组

    getData() {
        this.list = this.constMerchantData.map(item => {
         var children = item.children.filter(
          item2 => item2.merchant_name.indexOf(this.vague) > -1)
         if (children.length > 0) {
          return {
           name: item.name,
           children: children
          }
         } else {
          return {
           name: "",
           children: []
          }
         }
        })
       }
    

    源码下载

    展开全文
  • 背景 视图函数无法按ctrl+鼠标左键,自动跳转到对应html文件。但是程序启动是可以找到对应html模板。...3.如果1和2都了,还是不行,在pycharm中强制设置索引方式: 最终问题得以解决。 ...

    背景
    视图函数无法按ctrl+鼠标左键,自动跳转到对应的html文件。但是程序启动是可以找到对应的html模板。
    如图:
    在这里插入图片描述
    问题解决
    1.setting文件加入了app名称
    2.urls文件中加入app
    3.如果1和2都做了,还是不行,在pycharm中强制设置索引方式:
    在这里插入图片描述
    最终问题得以解决。

    展开全文
  • 相信大家在许多App中都见到过带字母索引的界面,比如我最近看到的这个开源控件: WaveSideBar 很酷是不是?!!!如果加在例如联系人列表界面上,大大提升了用户体验。 那么这个索引控件要怎么呢,说到底就是...

    相信大家在许多App中都见到过带字母索引的界面,比如我最近看到的这个开源控件:

    WaveSideBar

    WaveSideBarDemo

    很酷是不是?!!!如果加在例如联系人列表界面上,大大提升了用户体验。

    那么这个索引控件要怎么做呢,说到底就是自定义一个view,因为自身能力原因我并不能做出这样的效果,当然各位大神们可以自行去研究这类开源索引控件的源码。


    以我的能力,现在只能做这样的:

    SideBarDemo

    虽然简单,但是对于新手来说学习一番还是不错的。
    下面我们开始一步步写一个字母索引控件 SimpleSideBar


    准备一些知识

    这里推荐博主guolin的一系列文章

    http://blog.csdn.net/guolin_blog/article/details/12921889

    http://blog.csdn.net/guolin_blog/article/details/16330267

    http://blog.csdn.net/jdsjlzx/article/details/41113969

    第一步,创建类SimpleSideBar继承View

    public class SimpleSideBar extends View {
    
        public SimpleSideBar(Context context) {
            super(context);
        }
    
        public SimpleSideBar(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public SimpleSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    }

    第二步,声明所需要的变量

        // 索引字母数组
        private String[] alphabet = {
                "A", "B", "C", "D", "E", "F",
                "G", "H", "I", "J", "K", "L",
                "M", "N", "O", "P", "Q", "R",
                "S", "T", "U", "V", "W", "X",
                "Y", "Z"
        };
    
        // 当前选择的索引字母的下标
        private int currentChoosenAlphabetIndex=-1;
    
        // 画笔
        private Paint mPaint=new Paint();
    
        // 索引字母绘制大小
        private int alphabetTextSize=20;

    第三步,重写onDraw函数

    该函数是绘制函数,通过它将控件内容显现出来

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // 获得控件高度
            int viewHeight=getHeight();
            // 获得控件宽度
            int viewWidth=getWidth();
            // 控件高度除以索引字母个数得到每个索引字母的高度
            int heightPerAlphabet=viewHeight/alphabet.length;
    
            //通过循环每个索引字母,并绘制出来
            for (int i=0;i<alphabet.length;i++){
    
                // 设置画笔颜色、画笔绘制文字粗细和大小,设置抗锯齿
                mPaint.setColor(Color.BLACK);
                mPaint.setTypeface(Typeface.DEFAULT_BOLD);
                mPaint.setTextSize(alphabetTextSize);
                mPaint.setAntiAlias(true);
    
                // 如果当前选择的索引字母下标和循环到的索引字母下标相同
                if(currentChoosenAlphabetIndex==i){
    
                    // 设置画笔颜色,绘制文字大小和加粗
                    mPaint.setColor(Color.YELLOW);
                    mPaint.setTextSize(alphabetTextSize);
                    mPaint.setFakeBoldText(true);
    
                }
    
                // 索引字母的相对于控件的x坐标,此处算法结果为居中
                float xPos=viewWidth/2-mPaint.measureText(alphabet[i])/2;
                // 索引字母的相对于控件的y坐标,索引字母的高度乘以索引字母下标+1即为y坐标
                float yPos=heightPerAlphabet*i+heightPerAlphabet;
                // 绘制索引字母
                canvas.drawText(alphabet[i],xPos,yPos,mPaint);
                // 重置画笔,为绘制下一个索引字母做准备
                mPaint.reset();
    
            }
        }
    

    到这里,我们直接把该控件加入layout中已经可以显示出来,只是没有触摸到索引字母使ListView/RecyclerView滚动到相应位置的功能。

    如果要实现这个功能,我们需要重写dispatchTouchEvent函数来处理控件触摸事件,并且要对外提供一个接口来实现列表滚动到相应位置。

    下面我们继续实现它。

    第四步,提供一个接口

    首先定义一个接口

        public interface OnLetterTouchedChangeListener{
    
            void onTouchedLetterChange(String letterTouched);
    
        }

    接着声明一个接口变量,提供set方法

        OnLetterTouchedChangeListener onLetterTouchedChangeListener;
    
        public void setOnLetterTouchedChangeListener(OnLetterTouchedChangeListener onLetterTouchedChangeListener) {
            this.onLetterTouchedChangeListener = onLetterTouchedChangeListener;
        }
    

    第五步,重写dispatchTouchEvent函数

    该函数是控件触摸事件分发函数,当返回值为true时表示处理完毕不分发到下一级处理。

        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
    
            // 获得触摸后的动作
            int action = event.getAction();
            // 获得触摸点的Y轴坐标
            float touchYPos=event.getY();
    
            // 控件高度除以索引字母的个数得到每个索引字母的高度(这里进行int强转),触摸点的Y轴坐标除以每个索引字母的高度就得到触摸到的索引字母的下标
            int currentTouchIndex= (int) (touchYPos/getHeight()*alphabet.length);
    
    
            switch (action){
                // 当触摸的动作为按下或者按下移动时
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    // 设置背景颜色
                    setBackgroundResource(R.color.grey_600);
    
                    // 设置当前选的索引字母的下标值为当前选择的值
                    currentChoosenAlphabetIndex=currentTouchIndex;
    
                    // 如果接口存在和索引下标值合法,执行接口方法,传入当前触摸的索引字母,供外部调用接收
        if(onLetterTouchedChangeListener!=null&&currentTouchIndex<alphabet.length&&currentTouchIndex>-1){
                        onLetterTouchedChangeListener.onTouchedLetterChange(alphabet[currentTouchIndex]);
                    }
    
                    // 重新绘制控件,即重新执行onDraw函数
                    invalidate();
    
    
                    break;
                case MotionEvent.ACTION_UP:
    
                    setBackgroundResource(R.color.grey_50);
                    // 当停止触摸控件的时候,将当前选择的索引字母下标值设为-1
                    currentChoosenAlphabetIndex=-1;
                    invalidate();
                    break;
                default:break;
    
            }
    
            // 返回true表明该触摸事件处理完毕不分发出去
            return true;
        }

    第六步,使用自定义的SimpleSideBar控件

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/root"
        android:orientation="horizontal" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <LinearLayout
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
    
        </LinearLayout>
    
        <me.pwcong.simplesidebar.view.SimpleSideBar
            android:id="@+id/sideBar"
            android:layout_width="30dp"
            android:layout_height="wrap_content" />
    
    
    </LinearLayout>
    

    MainActivity.java

            ...
    
            sideBar= (SimpleSideBar) findViewById(R.id.sideBar);
    
            // 这里使用前面定义的接口
            sideBar.setOnLetterTouchedChangeListener(new SimpleSideBar.OnLetterTouchedChangeListener() {
                @Override
                public void onTouchedLetterChange(String letterTouched) {
    
                    // 获得触摸到的索引字母,再通过索引字母获取相应item的下标,执行列表滚动方法
                    int pos=simpleAdapter.getLetterPosition(letterTouched);
                    if(pos!=-1){
                        recyclerView.scrollToPosition(pos);
                    }
                }
            });
    

    到这里我们的简单索引字母控件完成了,SimpleSideBar 的 Demo 可以在这里下载

    https://github.com/pwcong/SimpleSideBar

    转载于:https://www.cnblogs.com/pwc1996/p/5957843.html

    展开全文
  • 前言:因疫情,近日在家无要事可。闲暇之余,想要用Kotlin,JetPack一个完整MVVM架构项目。于是它来了,如果,顺便能给个评论,加个关注,我将不胜感激! ...
  • Android 索引的实现

    2016-09-23 17:09:20
    在刚学习Android时候就想自己手写一个索引,但是当时还是彩笔,就一直没,昨天写了一个索引,今天贴出来,以备后用,先上图: 由于列表是我自己封装,可能必看到展示页面时候会有陌生感,但是并不复杂,很好...
  • 性能是为Windows Phone 7设备创建应用程序时要考虑一个重要因素。...尽管Silverlight已经针对WP7了优化,但是仍需要在SL应用程序中通过一定手段以进一步优化CPU和GPU。这篇文章介绍了一些可以...
  • 除此之外,最近,Google宣布将有一个单独移动web搜索索引。这革命性一步完全改变了移动app开发趋势,特别是从SEO和web app的角度。加速移动页面AMP改变web app 不正是说明android时代在慢慢变好吗? 面试题 一般...
  • 昨天公司APP项目上线时遇到一个奇葩问题,在这里简单一个记录,以避免如果再遇到类似问题再去花费时间寻找解决方案。 首先贴出cp_orders表中建立的索引 使用限制符limit时,mysql使用了idx_order_type索引,...
  • mui的索引列表实现

    2020-07-24 18:02:03
    别人博客,亲测有用,了微调 <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content=...
  • 今天在选择一条记录进行删除操作时,碰见index失败问题,如下:处理失败!错误信息:[SQLException ORA-01502 index VHL_V6.PK_WEB_APP_TGT_OBJ or partition of such index is in unusable state ]在网上查询...
  • 个三维策略(共三篇)这是汇总系列文章的索引文,方便链接查找。1 汇总篇简介这篇文章目的,是为了把之前某一个系列推文一个汇总,方便查找链接。“个三维策略”系列,是“三维谈判系统”第三个系列推文,...
  • 以上就是最终效果图,我尝试了各种方法都没办法在原生基础上解决问题,所以这里我采用方法是放弃vant自带的索引,全部用自己思路重新来一个类似功能。 wxml <view id='app'> <navbar pageName=...
  • 微信小程序手把手教你实现带...在Android和iOS应用开发的时候,我们经常在app中要用到带字母索引的城市选择功能,他们功能基本都大同小异,大概长下面的样子: 那么在微信小程序(文章后面统称小程序)中我们要怎...
  • iOS9中支持为app内容做索引以支持spotlight搜索,感觉是个很有心意功能。需要提到是这些索引是存在本地设备中,不会同步到icoloud中,更换了设备就没有了。 效果就是这样: 创建支持搜索内容 支持...
  • 可重用很重要 设计、构建、测试和维护一个...Python包索引 (PyPI) 具有广泛包,你可以在你自己Python程序中使用。查阅一下Django Packages中已经存在可重用应用,你可以结合它们到你项目。Django 自身...
  • 之前我们记录了一篇数据结构的的在线演示网站,具体见这篇,接下来我们分析一下,为什么mysql使用B+树做索引。 这里我们首先需要分析,mysql 一页可以存多少数据,一页默认 16K , 这里我们主键是 bigint , ...
  • 可重用很重要设计、构建、测试和维护...Python包索引 (PyPI) 具有广泛包,你可以在你自己Python程序中使用。查阅一下Django Packages中已经存在可重用应用,你可以结合它们到你项目。Django 自身也只是一...
  • Android 首字母索引

    千次阅读 2018-08-19 12:23:42
    由于之前的应用真的没有用到过首字母索引的功能,并且android没有提供对应的组件,也就没向这方面考虑过。但是现在的项目要求要有这项功能,并且在网上一搜会有很多,都挺好的,并且大致思想都差不多,所以我也...
  • 您可以使用此工具什么? 安装 $ npm install -g create-component-app 用法 $ cd ~ /my-projects $ create-component-app 有多种选择,可以从终端创建组件 创建不同种类组件: 无状态 类 纯 习俗 设置新...
  • 当日生产环境发布后两小时(项目未蓝绿发布),运营开始再公司群叫喊app运行卡死,瞬间感觉血压飙升100+。项目组整体不休息开始查找原因。步骤如下: 先用zb查看线上代码服务器cpu飙高到40以上(运维人员用是...
  • app登陆接口进行优化

    2015-10-19 23:35:00
    发现app_user这个表uid字段没有做索引,导致删除语句,没有用到索引,全表扫描。性能测试,1万并发,需要30s左右。加了索引后要30ms。 学习了,删除也需要用索引。 转载于:...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 282
精华内容 112
关键字:

做索引的app