国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android通用索引欄實現代碼

2019-12-12 04:18:53
字體:
來源:轉載
供稿:網友

偶爾看到之前寫過的代碼,感覺好多東西幾乎在很多項目中都要用到,雖然每個項目的需求和設計都不同,不過實現的效果都是一樣的,可能只是數據格式和一些顏色等的細微差距.但是有的時候因為一個小改變,就要去重復的修改代碼,麻煩不說,也容易導致新的問題和BUG.

就拿忽然想到的索引欄來說,幾乎寫過的項目中都用到了,比如城市選擇、聯系人等等.這些地方全都需要用到索引欄,但是用法都是一樣的.翻看了幾處之前寫過的代碼,發現每次用到索引欄,都要重新去寫方法來處理數據或者對數據的索引進行提取這些,做法也都大同小異.于是乎,嘗試著重構一下這部分,也方便之后的使用.

先看一下效果圖:

實現

索引欄的實現,網上有很多例子,也比較簡單,就不做過多解釋.因為在不同項目中可能涉及到索引欄字體顏色、大小不同等問題,所以把之前用到的代碼做一下修改,提取出一些自定義屬性,方便修改,就不必每次都去代碼中修改,也避免影響到其他人的使用.直接看一下代碼,在attr中定義一些自定義屬性,如下:

attr:

 <?xml version="1.0" encoding="utf-8"?><resources>  <!--SideBar相關-->  <!--普通時的顏色-->  <attr name="normalColor" format="color"/>  <!--選中時的顏色-->  <attr name="chooseColor" format="color"/>  <!--普通時的背景圖-->  <attr name="normalBackground" format="reference"/>  <!--選中時的背景圖-->  <attr name="chooseBackground" format="reference"/>  <!--索引欄文字大小-->  <attr name="sideTextSize" format="dimension"/>  <declare-styleable name="SideBar">    <attr name="normalColor"/>    <attr name="chooseColor"/>    <attr name="normalBackground"/>    <attr name="chooseBackground"/>    <attr name="sideTextSize"/>  </declare-styleable></resources>

把顏色文字大小等屬性提取出來方便修改.然后看一下SideBar

SideBar:

package com.example.junweiliu.commindexdemo.widget;import android.annotation.TargetApi;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.Typeface;import android.graphics.drawable.Drawable;import android.os.Build;import android.util.AttributeSet;import android.util.TypedValue;import android.view.MotionEvent;import android.view.View;import android.widget.TextView;import com.example.junweiliu.commindexdemo.R;/** * Created by junweiliu on 16/11/24. */public class SideBar extends View {  /**   * 點擊回調   */  private OnTouchingLetterChangedListener onTouchingLetterChangedListener;  /**   * 26字母   */  public static String[] letterStrs = {"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 choose = -1;  /**   * 字母畫筆   */  private Paint paint = new Paint();  /**   * 顯示的TextView   */  private TextView mTextDialog;  /**   * 普通時的顏色   */  private int normalColor;  /**   * 選中的顏色   */  private int chooseColor;  /**   * 普通時的背景   */  private Drawable normalBackground;  /**   * 選中時的背景   */  private Drawable chooseBackground;  /**   * 文字大小   */  private float textSize;  /**   * 邊框   */  private Rect mRect;  public SideBar(Context context, AttributeSet attrs) {    this(context, attrs, 0);  }  public SideBar(Context context, AttributeSet attrs, int defStyle) {    super(context, attrs, defStyle);    // 獲取自定義屬性    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SideBar);    normalColor = ta.getColor(R.styleable.SideBar_normalColor, Color.GRAY);    chooseColor = ta.getColor(R.styleable.SideBar_chooseColor, Color.RED);    normalBackground = ta.getDrawable(R.styleable.SideBar_normalBackground);    chooseBackground = ta.getDrawable(R.styleable.SideBar_chooseBackground);    textSize = ta.getDimension(R.styleable.SideBar_sideTextSize, TypedValue        .applyDimension(TypedValue.COMPLEX_UNIT_SP, 13,            getResources().getDisplayMetrics()));    ta.recycle();    init();  }  /**   * 為SideBar設置顯示字母的TextView   *   * @param mTextDialog   */  public void setTextView(TextView mTextDialog) {    this.mTextDialog = mTextDialog;  }  /**   * 設置   *   * @param letter   */  public void setLetter(String[] letter) {    this.letterStrs = letter;    invalidate();    requestLayout();  }  /**   * 初始化參數   */  @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  private void init() {    paint.setColor(normalColor);    paint.setTypeface(Typeface.DEFAULT_BOLD);    paint.setAntiAlias(true);    paint.setTextSize(textSize);    // 獲取單個繪制的rect,用于獲取單個繪制項的高度    mRect = new Rect();    paint.getTextBounds("A", 0, "A".length(), mRect);  }  /**   * 繪制   *   * @param canvas   */  protected void onDraw(Canvas canvas) {    super.onDraw(canvas);    // 獲取焦點改變背景顏色.    int height = getHeight() - getPaddingTop() - getPaddingBottom();// 獲取對應高度    int width = getWidth(); // 獲取對應寬度    int singleHeight = height / letterStrs.length;// 獲取每一個字母的高度    for (int i = 0; i < letterStrs.length; i++) {      // 選中的狀態      if (i == choose) {        paint.setColor(chooseColor);        paint.setFakeBoldText(true);      }      // x坐標等于中間-字符串寬度的一半.      float xPos = width / 2 - paint.measureText(letterStrs[i]) / 2;      float yPos = singleHeight * i + singleHeight;      canvas.drawText(letterStrs[i], xPos, yPos, paint);      paint.reset();// 重置畫筆      init();    }  }  @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  @Override  public boolean dispatchTouchEvent(MotionEvent event) {    final int action = event.getAction();    // 點擊的y坐標    final float y = event.getY();    final int oldChoose = choose;    final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;    // 獲取當前點擊的字母位置,點擊位置的y坐標比上總的高度相當于點擊的位置比上全部位置(c / b.length = y / getHeight())    final int currChoose = (int) (y / getHeight() * letterStrs.length);    switch (action) {      case MotionEvent.ACTION_UP:        // 重置背景色        if (null != normalBackground) {          setBackground(normalBackground);        } else {          setBackgroundColor(Color.argb(0, 0, 0, 0));        }        // 抬起時置為-1        choose = -1;        invalidate();        if (mTextDialog != null) {          mTextDialog.setVisibility(View.INVISIBLE);        }        break;      default:        // 設置背景色        if (null != chooseBackground) {          setBackground(chooseBackground);        }        if (oldChoose != currChoose) {          if (currChoose >= 0 && currChoose < letterStrs.length) {            if (null != listener) {              listener.onTouchingLetterChanged(letterStrs[currChoose]);            }            if (null != mTextDialog) {              mTextDialog.setText(letterStrs[currChoose]);              mTextDialog.setVisibility(View.VISIBLE);            }            // 設置選中的位置為當前位置            choose = currChoose;            invalidate();          }        }        break;    }    return true;  }  /**   * 向外公開的方法   *   * @param onTouchingLetterChangedListener   */  public void setOnTouchingLetterChangedListener(      OnTouchingLetterChangedListener onTouchingLetterChangedListener) {    this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;  }  /**   * 回調接口   *   * @author coder   */  public interface OnTouchingLetterChangedListener {    void onTouchingLetterChanged(String s);  }  /**   * 測量   *   * @param widthMeasureSpec   * @param heightMeasureSpec   */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);    // 當高度為自適應時,高度為字母高度*字母數量*2 即間隔為單位高度    int wrapHeight = letterStrs.length * (mRect.height() * 2);    // 當寬度為自適應使,寬度為字母寬度*2    int warpWidth = mRect.width() * 2;    setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : warpWidth        , (heightMode == MeasureSpec.EXACTLY) ? sizeHeight            //wrap_content時的高度            : wrapHeight);  }}

很簡單,只是提取出來了一些自定義屬性,沒什么可說的,接下來分析一下如何讓索引欄變得通用,先來想一下索引欄一般的寫法.首先拿到一個數據源,然后對這個數據源進行處理,從數據源中提取出首字母當做索引(當然數據源中可能已經含有首字母或者索引等字段),有了索引之后,再在適配器中進行判斷來控制是否顯示索引標題(我的做法是判斷第一次出現當前索引的數據源位置和當前位置是否相同,如果相同則顯示索引標題),處理完索引標題的顯示和隱藏,最后就是跟索引欄進行綁定(實現索引欄的回調方法并做相關處理).大體步驟就是這樣,接下來就是找一下處理不同的地方,比對了一下,發現問題基本都是出現在數據格式不同上,有的數據的索引字段可能叫Letter,有的可能叫LetterName,這就導致了每次對這些數據進行處理時,都要重新寫方法或者修改方法,使得這些方法不共用.那怎么解決一下這個問題呢,最開始想到的是寫一個抽象類,然后用一個抽象方法getLetterName()來約束索引.每個需要用到索引欄的Bean都去繼承這個抽象類,重寫這個抽象方法,從而達到統一約束索引值的效果,也就解決了索引值字段不同的問題,這樣就可以用一個公共的方法來處理不同的數據源.后來又考慮了一下,這個地方其實用接口會更加合適一點,接口靈活性更大,而且也是面向接口編程的一種體現.分析了這么多,來具體代碼實現一下,提出一個接口,之后所有需要用到索引的數據Bean去實現這個接口中的getLetterName()方法并且重寫這個方法來返回索引值即可.

接口SideBase:

package com.example.junweiliu.commindexdemo.bean;/** * Created by junweiliu on 16/11/21. */public interface SideBase {  String getLetterName();}

然后數據Bean去實現這個接口,例如比較常見的CityBean:

package com.example.junweiliu.commindexdemo.bean;public class CityBean implements SideBase {  /**   * 城市名   */  private String cityName;  /**   * 首字母   */  private String cityHeader;  /**   * 城市信息   */  private String cityMes;  public CityBean(String cityName, String cityHeader, String cityMes) {    this.cityName = cityName;    this.cityHeader = cityHeader;    this.cityMes = cityMes;  }  public String getCityName() {    return cityName;  }  public void setCityName(String cityName) {    this.cityName = cityName;  }  public String getCityHeader() {    return cityHeader;  }  public void setCityHeader(String cityHeader) {    this.cityHeader = cityHeader;  }  public String getCityMes() {    return cityMes;  }  public void setCityMes(String cityMes) {    this.cityMes = cityMes;  }  /**   * 獲取索引   *   * @return   */  @Override  public String getLetterName() {    return cityHeader;  }}

在getLetterName()方法中去返回索引值.

接下來就可以去寫一些公共的處理方法.比如

  /**   * 根據當前選中的項獲取其第一次出現該項首字母的位置   *   * @param position 當前選中的位置   * @param datas  數據源   * @return   */  public static int getPositionForSection(int position, List<? extends SideBase> datas) {    // 當前選中的項    SideBase sideBase = datas.get(position);    for (int i = 0; i < datas.size(); i++) {      String firstStr = datas.get(i).getLetterName().toUpperCase();      // 返回第一次出現該項首字母的位置      if (firstStr.equals(sideBase.getLetterName())) {        return i;      }    }    return -1;  }

因為使用的接口,這里就可以用通配符?的方式來對數據進行處理,只關心和處理getLetterName()方法即可.還可以做其他處理:

  /**   * 獲取所選中的索引在列表中的位置   *   * @param list   * @param letter   * @return   */  public static int getLetterPosition(List<? extends SideBase> list, String letter) {    int position = -1;    if (list != null && !list.isEmpty() && !"".equals(letter)) {      for (int i = 0; i < list.size(); i++) {        SideBase bean = list.get(i);        if (bean.getLetterName().equals(letter)) {          position = i;          break;        }      }    }    return position;  }  /**   * 篩選出數據源中所包含的全部索引值   *   * @param list   * @return   */  public static String[] getLetters(List<? extends SideBase> list) {    List<String> letters = new ArrayList<>();    if (list != null && !list.isEmpty()) {      for (int i = 0; i < list.size(); i++) {        if (!letters.contains(list.get(i).getLetterName())) {          letters.add(list.get(i).getLetterName());        }      }    }    return (String[]) letters.toArray(new String[letters.size()]);  }

通過分析和重構之后,這些之前不能公用的方法就變得通用起來,很方便,之后用起來也會特別簡單、舒心.

完整代碼

公共處理類

CommUtil:

package com.example.junweiliu.commindexdemo.util;import com.example.junweiliu.commindexdemo.bean.SideBase;import java.util.ArrayList;import java.util.List;/** * Created by junweiliu on 16/11/24. */public class CommUtil {  /**   * 根據當前選中的項獲取其第一次出現該項首字母的位置   *   * @param position 當前選中的位置   * @param datas  數據源   * @return   */  public static int getPositionForSection(int position, List<? extends SideBase> datas) {    // 當前選中的項    SideBase sideBase = datas.get(position);    for (int i = 0; i < datas.size(); i++) {      String firstStr = datas.get(i).getLetterName().toUpperCase();      // 返回第一次出現該項首字母的位置      if (firstStr.equals(sideBase.getLetterName())) {        return i;      }    }    return -1;  }  /**   * 獲取所選中的索引在列表中的位置   *   * @param list   * @param letter   * @return   */  public static int getLetterPosition(List<? extends SideBase> list, String letter) {    int position = -1;    if (list != null && !list.isEmpty() && !"".equals(letter)) {      for (int i = 0; i < list.size(); i++) {        SideBase bean = list.get(i);        if (bean.getLetterName().equals(letter)) {          position = i;          break;        }      }    }    return position;  }  /**   * 篩選出數據源中所包含的全部索引值   *   * @param list   * @return   */  public static String[] getLetters(List<? extends SideBase> list) {    List<String> letters = new ArrayList<>();    if (list != null && !list.isEmpty()) {      for (int i = 0; i < list.size(); i++) {        if (!letters.contains(list.get(i).getLetterName())) {          letters.add(list.get(i).getLetterName());        }      }    }    return (String[]) letters.toArray(new String[letters.size()]);  }}

適配器CityAdapter:

package com.example.junweiliu.commindexdemo.adapter;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import com.example.junweiliu.commindexdemo.R;import com.example.junweiliu.commindexdemo.bean.CityBean;import com.example.junweiliu.commindexdemo.util.CommUtil;import java.util.List;/** * Created by junweiliu on 16/11/24. */public class CityAdapter extends BaseAdapter {  /**   * 上下文   */  private Context context;  /**   * 布局加載器   */  private LayoutInflater mInflater;  /**   * 數據源   */  private List<CityBean> cityBeanList;  /**   * 構造方法   *   * @param context   * @param cityBeanList   */  public CityAdapter(Context context, List<CityBean> cityBeanList) {    this.context = context;    this.cityBeanList = cityBeanList;  }  @Override  public int getCount() {    return cityBeanList.size();  }  @Override  public Object getItem(int i) {    return cityBeanList.get(i);  }  @Override  public long getItemId(int i) {    return i;  }  @Override  public View getView(int position, View convertView, ViewGroup viewGroup) {    ViewHolder viewHolder = null;    CityBean bean = cityBeanList.get(position);    if (convertView == null) {      convertView = mInflater.from(context).inflate(R.layout.item_city, null);      viewHolder = new ViewHolder();      viewHolder.headerTv = (TextView) convertView.findViewById(R.id.tv_item_citys_header);      viewHolder.contentTv = (TextView) convertView.findViewById(R.id.tv_item_citys_context);      convertView.setTag(viewHolder);    } else {      viewHolder = (ViewHolder) convertView.getTag();    }    // 如果當前位置為第一次出現該類首字母的位置,則顯示headerTv    if (position == CommUtil.getPositionForSection(position, cityBeanList)) {      viewHolder.contentTv.setVisibility(View.VISIBLE);      viewHolder.headerTv.setVisibility(View.VISIBLE);      viewHolder.headerTv.setText(bean.getLetterName());    } else {      viewHolder.headerTv.setVisibility(View.GONE);      viewHolder.contentTv.setVisibility(View.VISIBLE);    }    viewHolder.contentTv.setText(bean.getCityName());    return convertView;  }  /**   * vh   */  class ViewHolder {    TextView headerTv;    TextView contentTv;  }}

MainActivity:

package com.example.junweiliu.commindexdemo;import android.app.Activity;import android.os.Bundle;import android.widget.ListView;import android.widget.TextView;import com.example.junweiliu.commindexdemo.adapter.CityAdapter;import com.example.junweiliu.commindexdemo.bean.CityBean;import com.example.junweiliu.commindexdemo.util.CommUtil;import com.example.junweiliu.commindexdemo.widget.SideBar;import java.util.ArrayList;import java.util.List;public class MainActivity extends Activity {  /**   * 城市列表數據   */  private List<CityBean> cityBeanList = new ArrayList<>();  /**   * 城市lv   */  private ListView cityList;  /**   * 索引欄   */  private SideBar mSideBar;  /**   * 顯示的tv   */  private TextView mShowTv;  /**   * 適配器   */  private CityAdapter mCityAdapter;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    initData();    initView();  }  /**   * 初始化數據   */  private void initData() {    CityBean bean1 = new CityBean("安徽", "A", "安徽故事");    CityBean bean2 = new CityBean("安徽1", "A", "安徽1故事");    CityBean bean3 = new CityBean("安徽2", "A", "安徽2故事");    CityBean bean4 = new CityBean("北京", "B", "北京故事");    CityBean bean5 = new CityBean("北京1", "B", "北京1故事");    CityBean bean6 = new CityBean("北京2", "B", "北京2故事");    CityBean bean7 = new CityBean("重慶", "C", "重慶故事");    CityBean bean8 = new CityBean("重慶1", "C", "重慶1故事");    CityBean bean9 = new CityBean("重慶2", "C", "重慶2故事");    CityBean bean10 = new CityBean("貴州", "G", "貴州故事");    CityBean bean11 = new CityBean("貴州1", "G", "貴州2故事");    CityBean bean12 = new CityBean("貴州2", "G", "貴州3故事");    CityBean bean13 = new CityBean("天津", "T", "天津故事");    CityBean bean14 = new CityBean("天津1", "T", "天津1故事");    CityBean bean15 = new CityBean("天津2", "T", "天津2故事");    cityBeanList.add(bean1);    cityBeanList.add(bean2);    cityBeanList.add(bean3);    cityBeanList.add(bean1);    cityBeanList.add(bean2);    cityBeanList.add(bean3);    cityBeanList.add(bean4);    cityBeanList.add(bean5);    cityBeanList.add(bean6);    cityBeanList.add(bean4);    cityBeanList.add(bean5);    cityBeanList.add(bean6);    cityBeanList.add(bean7);    cityBeanList.add(bean8);    cityBeanList.add(bean9);    cityBeanList.add(bean7);    cityBeanList.add(bean8);    cityBeanList.add(bean9);    cityBeanList.add(bean10);    cityBeanList.add(bean11);    cityBeanList.add(bean12);    cityBeanList.add(bean10);    cityBeanList.add(bean11);    cityBeanList.add(bean12);    cityBeanList.add(bean13);    cityBeanList.add(bean14);    cityBeanList.add(bean15);    cityBeanList.add(bean13);    cityBeanList.add(bean14);    cityBeanList.add(bean15);  }  /**   * 初始化控件   */  private void initView() {    cityList = (ListView) findViewById(R.id.lv_city);    mSideBar = (SideBar) findViewById(R.id.sb_city);    mShowTv = (TextView) findViewById(R.id.tv_city_show);    mCityAdapter = new CityAdapter(MainActivity.this, cityBeanList);    cityList.setAdapter(mCityAdapter);    // 設置需要顯示的索引欄內容    mSideBar.setLetter(CommUtil.getLetters(cityBeanList));    // 設置需要顯示的提示框    mSideBar.setTextView(mShowTv);    mSideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {      @Override      public void onTouchingLetterChanged(String s) {        int position = CommUtil.getLetterPosition(cityBeanList, s);        if (position != -1) {          cityList.setSelection(position);        }      }    });  }}

布局文件activity_main:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.junweiliu.commindexdemo.MainActivity">  <!--城市列表-->  <ListView      android:id="@+id/lv_city"      android:layout_width="match_parent"      android:layout_height="match_parent">  </ListView>  <!--索引欄-->  <com.example.junweiliu.commindexdemo.widget.SideBar      android:id="@+id/sb_city"      android:layout_width="wrap_content"      android:layout_height="200dp"      android:layout_alignParentRight="true"      android:layout_centerVertical="true"      app:sideTextSize="13sp"/>  <!--顯示的字母-->  <TextView      android:id="@+id/tv_city_show"      android:layout_width="80dp"      android:layout_height="80dp"      android:layout_centerInParent="true"      android:background="#3F51B5"      android:gravity="center"      android:textColor="#ffffff"      android:textSize="25sp"      android:visibility="gone"  /></RelativeLayout>

適配器布局item_city:

<?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_citys_header"      android:layout_width="match_parent"      android:layout_height="wrap_content"      android:background="#E3E3E3"      android:padding="15dp"      android:text="A"      android:textColor="#666666"      android:textSize="14sp"      android:visibility="gone"/>  <!-- 內容 -->  <TextView      android:id="@+id/tv_item_citys_context"      android:layout_width="match_parent"      android:layout_height="wrap_content"      android:background="#ffffff"      android:padding="15dp"      android:textColor="#000000"      android:textSize="14sp"      android:visibility="gone"/></LinearLayout>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 安图县| 通海县| 兴隆县| 霍邱县| 拜城县| 苏尼特右旗| 新昌县| 通许县| 盐津县| 西城区| 汉中市| 曲阜市| 永春县| 静乐县| 湘潭市| 山东省| 新津县| 三河市| 四平市| 安西县| 沂水县| 肥西县| 平乡县| 姜堰市| 顺平县| 望城县| 萝北县| 锦州市| 凌海市| 安泽县| 博乐市| 垦利县| 上思县| 偏关县| 岳西县| 宁都县| 康定县| 神池县| 明光市| 太白县| 福泉市|