<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自定義View實(shí)現(xiàn)字母導(dǎo)航控件

          共 5257字,需瀏覽 11分鐘

           ·

          2020-08-31 10:35

          PS:如果不能嚴(yán)格內(nèi)化自己,就沒有足夠的心理能量進(jìn)行深度工作。
          今天分享一個(gè)以前實(shí)現(xiàn)的通訊錄字母導(dǎo)航控件,下面自定義一個(gè)類似通訊錄的字母導(dǎo)航 View,可以知道需要自定義的幾個(gè)要素,如繪制字母指示器、繪制文字、觸摸監(jiān)聽、坐標(biāo)計(jì)算等,自定義完成之后能夠達(dá)到的功能如下:
          • 完成列表數(shù)據(jù)與字母之間的相互聯(lián)動(dòng);

          • 支持布局文件屬性配置;

          • 在布局文件中能夠配置相關(guān)屬性,如字母顏色、字母字體大小、字母指示器顏色等屬性。

          主要內(nèi)容如下:
          1. 自定義屬性

          2. Measure測(cè)量

          3. 坐標(biāo)計(jì)算

          4. 繪制

          5. 顯示效果

          自定義屬性

          在 value 下面創(chuàng)建 attr.xml ,在里面配置需要自定義的屬性,具體如下:

          <resources>
          ????<declare-styleable?name="LetterView">
          ????????
          ????????<attr?name="letterTextColor"?format="color"?/>
          ????????
          ????????<attr?name="letterTextSize"?format="dimension"?/>
          ????????
          ????????<attr?name="letterTextBackgroundColor"?format="color"?/>
          ????????
          ????????<attr?name="letterEnableIndicator"?format="boolean"?/>
          ????????
          ????????<attr?name="letterIndicatorColor"?format="color"?/>
          ????declare-styleable>
          resources>
          然后在相應(yīng)的構(gòu)造方法中獲取這些屬性并進(jìn)行相關(guān)屬性的設(shè)置,具體如下:
          public?LetterView(Context?context,?@Nullable?AttributeSet?attrs)?{
          ????super(context,?attrs);
          ????//獲取屬性
          ????TypedArray?array?=?context.obtainStyledAttributes(attrs,?R.styleable.LetterView);
          ????int?letterTextColor?=?array.getColor(R.styleable.LetterView_letterTextColor,?Color.RED);
          ????int?letterTextBackgroundColor?=?array.getColor(R.styleable.LetterView_letterTextBackgroundColor,?Color.WHITE);
          ????int?letterIndicatorColor?=?array.getColor(R.styleable.LetterView_letterIndicatorColor,?Color.parseColor("#333333"));
          ????float?letterTextSize?=?array.getDimension(R.styleable.LetterView_letterTextSize,?12);
          ????enableIndicator?=?array.getBoolean(R.styleable.LetterView_letterEnableIndicator,?true);

          ????//默認(rèn)設(shè)置
          ????mContext?=?context;
          ????mLetterPaint?=?new?Paint();
          ????mLetterPaint.setTextSize(letterTextSize);
          ????mLetterPaint.setColor(letterTextColor);
          ????mLetterPaint.setAntiAlias(true);

          ????mLetterIndicatorPaint?=?new?Paint();
          ????mLetterIndicatorPaint.setStyle(Paint.Style.FILL);
          ????mLetterIndicatorPaint.setColor(letterIndicatorColor);
          ????mLetterIndicatorPaint.setAntiAlias(true);

          ????setBackgroundColor(letterTextBackgroundColor);

          ????array.recycle();
          }

          Measure測(cè)量

          要想精確的控制自定義的尺寸以及坐標(biāo),必須要測(cè)量出當(dāng)前自定義 View 的寬高,然后才可以通過測(cè)量到的尺寸計(jì)算相關(guān)坐標(biāo),具體測(cè)量過程就是繼承 View 重寫 omMeasure() 方法完成測(cè)量,關(guān)鍵代碼如下:
          @Override
          protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{
          ????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);
          ????//獲取寬高的尺寸大小
          ????int?widthSize?=?MeasureSpec.getSize(widthMeasureSpec);
          ????int?heightSize?=?MeasureSpec.getSize(heightMeasureSpec);
          ????//wrap_content默認(rèn)寬高
          ????@SuppressLint("DrawAllocation")?Rect?mRect?=?new?Rect();
          ????mLetterPaint.getTextBounds("A",?0,?1,?mRect);
          ????mWidth?=?mRect.width()?+?dpToPx(mContext,?12);
          ????int?mHeight?=?(mRect.height()?+?dpToPx(mContext,?5))?*?letters.length;

          ????if?(getLayoutParams().width?==?ViewGroup.LayoutParams.WRAP_CONTENT?&&
          ????????????getLayoutParams().height?==?ViewGroup.LayoutParams.WRAP_CONTENT)?{
          ????????setMeasuredDimension(mWidth,?mHeight);
          ????}?else?if?(getLayoutParams().width?==?ViewGroup.LayoutParams.WRAP_CONTENT)?{
          ????????setMeasuredDimension(mWidth,?heightSize);
          ????}?else?if?(getLayoutParams().height?==?ViewGroup.LayoutParams.WRAP_CONTENT)?{
          ????????setMeasuredDimension(widthSize,?mHeight);
          ????}

          ????mWidth?=?getMeasuredWidth();
          ????int?averageItemHeight?=?getMeasuredHeight()?/?28;
          ????int?mOffset?=?averageItemHeight?/?30;?//界面調(diào)整
          ????mItemHeight?=?averageItemHeight?+?mOffset;
          }

          坐標(biāo)計(jì)算

          自定義 View 實(shí)際上就是在 View 上找到合適的位置,將自定義的元素有序的繪制出來即可,繪制過程最困難的就是如何根據(jù)具體需求計(jì)算合適的左邊,至于繪制都是 API 的調(diào)用,只要坐標(biāo)位置計(jì)算好了,自定義 View 繪制這一塊應(yīng)該就沒有問題了,下面的圖示主要是標(biāo)注了字母指示器繪制的中心位置坐標(biāo)的計(jì)算以及文字繪制的起點(diǎn)位置計(jì)算,繪制過程中要保證文字在指示器中心位置,參考如下:
          自定義字母導(dǎo)航

          繪制

          自定義 View 的繪制操作都是在 onDraw() 方法中進(jìn)行的,這里主要使用到圓的繪制以及文字的繪制,具體就是 drawCircle() 和 drawText() 方法的使用,為避免文字被遮擋,需繪制字母指示器,然后再繪制字母,代碼參考如下:
          @Override
          protected?void?onDraw(Canvas?canvas)?{
          ????//獲取字母寬高
          ????@SuppressLint("DrawAllocation")?Rect?rect?=?new?Rect();
          ????mLetterPaint.getTextBounds("A",?0,?1,?rect);
          ????int?letterWidth?=?rect.width();
          ????int?letterHeight?=?rect.height();

          ????//繪制指示器
          ????if?(enableIndicator){
          ????????for?(int?i?=?1;?i?1;?i++)?{
          ????????????if?(mTouchIndex?==?i)?{
          ????????????????canvas.drawCircle(0.5f?*?mWidth,?i?*?mItemHeight?-?0.5f?*?mItemHeight,?0.5f?*?mItemHeight,?mLetterIndicatorPaint);
          ????????????}
          ????????}
          ????}
          ????//繪制字母
          ????for?(int?i?=?1;?i?1;?i++)?{
          ????????canvas.drawText(letters[i?-?1],?(mWidth?-?letterWidth)?/?2,?mItemHeight?*?i?-?0.5f?*?mItemHeight?+?letterHeight?/?2,?mLetterPaint);
          ????}
          }
          到此為止,可以說 View 的基本繪制結(jié)束了,現(xiàn)在使用自定義的 View 界面能夠顯示出來了,只是還沒有添加相關(guān)的事件操作,下面將在 View 的觸摸事件里實(shí)現(xiàn)相關(guān)邏輯。

          Touch事件處理

          為了判斷手指當(dāng)前所在位置對(duì)應(yīng)的是哪一個(gè)字母,需要獲取當(dāng)前觸摸的坐標(biāo)位置來計(jì)算字母索引,重新 onTouchEvent() 方法,監(jiān)聽 MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE 來計(jì)算索引位置,監(jiān)聽 MotionEvent.ACTION_UP 將獲得結(jié)果回調(diào)出去,具體參考如下:
          @Override
          public?boolean?onTouchEvent(MotionEvent?event)?{
          ????switch?(event.getAction())?{
          ????????case?MotionEvent.ACTION_DOWN:
          ????????case?MotionEvent.ACTION_MOVE:
          ????????????isTouch?=?true;
          ????????????int?y?=?(int)?event.getY();
          ????????????Log.i("onTouchEvent","--y->"?+?y?+?"-y-dp-->"?+?DensityUtil.px2dp(getContext(),?y));
          ????????????int?index?=?y?/?mItemHeight;

          ????????????if?(index?!=?mTouchIndex?&&?index?28?&&?index?>?0)?{
          ????????????????mTouchIndex?=?index;
          ????????????????Log.i("onTouchEvent","--mTouchIndex->"?+?mTouchIndex?+?"--position->"?+?mTouchIndex);
          ????????????}

          ????????????if?(mOnLetterChangeListener?!=?null?&&?mTouchIndex?>?0)?{
          ????????????????mOnLetterChangeListener.onLetterListener(letters[mTouchIndex?-?1]);
          ????????????}

          ????????????invalidate();
          ????????????break;
          ????????case?MotionEvent.ACTION_UP:
          ????????????isTouch?=?false;
          ????????????if?(mOnLetterChangeListener?!=?null?&&?mTouchIndex?>?0)?{
          ????????????????mOnLetterChangeListener.onLetterDismissListener();
          ????????????}
          ????????????break;
          ????}
          ????return?true;
          }
          到此為止,View 的自定義關(guān)鍵部分基本完成。

          數(shù)據(jù)組裝

          字母導(dǎo)航的基本思路是將某個(gè)需要與字母匹配的字段轉(zhuǎn)換為對(duì)應(yīng)的字母,然后按照該字段對(duì)數(shù)據(jù)進(jìn)行排序,最終使得通過某個(gè)數(shù)據(jù)字段的首字母就可以批匹配到相同首字母的數(shù)據(jù)了,這里將漢字轉(zhuǎn)化為拼音使用的是 pinyin4j-2.5.0.jar ,然后對(duì)數(shù)據(jù)項(xiàng)按照首字母進(jìn)行排序?qū)?shù)據(jù)展示到出來即可,漢字裝換為拼音如下:
          //漢字轉(zhuǎn)換為拼音
          public?static?String?getChineseToPinyin(String?chinese)?{
          ????StringBuilder?builder?=?new?StringBuilder();
          ????HanyuPinyinOutputFormat?format?=?new?HanyuPinyinOutputFormat();
          ????format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
          ????format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

          ????char[]?charArray?=?chinese.toCharArray();
          ????for?(char?aCharArray?:?charArray)?{
          ????????if?(Character.isSpaceChar(aCharArray))?{
          ????????????continue;
          ????????}
          ????????try?{
          ????????????String[]?pinyinArr?=?PinyinHelper.toHanyuPinyinStringArray(aCharArray,?format);
          ????????????if?(pinyinArr?!=?null)?{
          ????????????????builder.append(pinyinArr[0]);
          ????????????}?else?{
          ????????????????builder.append(aCharArray);
          ????????????}
          ????????}?catch?(BadHanyuPinyinOutputFormatCombination?badHanyuPinyinOutputFormatCombination)?{
          ????????????badHanyuPinyinOutputFormatCombination.printStackTrace();
          ????????????builder.append(aCharArray);
          ????????}
          ????}
          ????return?builder.toString();
          }
          至于數(shù)據(jù)排序使用 Comparator 接口即可,這里就不在贅述了,具體獲取文末源碼鏈接查看。

          顯示效果

          顯示效果如下:
          可以在公眾號(hào)回復(fù)關(guān)鍵字【MLetterView】查看源碼。
          推薦閱讀:
          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一级a毛一级a做视频 | 中午字幕在线观看 | 午夜精品视频成人精品视频 | 美女操逼黄色 | 日韩精品一区二区三区四区 |