Android通用索引欄實(shí)現(xiàn)代碼
偶爾看到之前寫(xiě)過(guò)的代碼,感覺(jué)好多東西幾乎在很多項(xiàng)目中都要用到,雖然每個(gè)項(xiàng)目的需求和設(shè)計(jì)都不同,不過(guò)實(shí)現(xiàn)的效果都是一樣的,可能只是數(shù)據(jù)格式和一些顏色等的細(xì)微差距.但是有的時(shí)候因?yàn)橐粋€(gè)小改變,就要去重復(fù)的修改代碼,麻煩不說(shuō),也容易導(dǎo)致新的問(wèn)題和BUG.
就拿忽然想到的索引欄來(lái)說(shuō),幾乎寫(xiě)過(guò)的項(xiàng)目中都用到了,比如城市選擇、聯(lián)系人等等.這些地方全都需要用到索引欄,但是用法都是一樣的.翻看了幾處之前寫(xiě)過(guò)的代碼,發(fā)現(xiàn)每次用到索引欄,都要重新去寫(xiě)方法來(lái)處理數(shù)據(jù)或者對(duì)數(shù)據(jù)的索引進(jìn)行提取這些,做法也都大同小異.于是乎,嘗試著重構(gòu)一下這部分,也方便之后的使用.
先看一下效果圖:

實(shí)現(xiàn)
索引欄的實(shí)現(xiàn),網(wǎng)上有很多例子,也比較簡(jiǎn)單,就不做過(guò)多解釋.因?yàn)樵诓煌?xiàng)目中可能涉及到索引欄字體顏色、大小不同等問(wèn)題,所以把之前用到的代碼做一下修改,提取出一些自定義屬性,方便修改,就不必每次都去代碼中修改,也避免影響到其他人的使用.直接看一下代碼,在attr中定義一些自定義屬性,如下:
attr:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--SideBar相關(guān)-->
<!--普通時(shí)的顏色-->
<attr name="normalColor" format="color"/>
<!--選中時(shí)的顏色-->
<attr name="chooseColor" format="color"/>
<!--普通時(shí)的背景圖-->
<attr name="normalBackground" format="reference"/>
<!--選中時(shí)的背景圖-->
<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>
把顏色文字大小等屬性提取出來(lái)方便修改.然后看一下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 {
/**
* 點(diǎn)擊回調(diào)
*/
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", "#"};
/**
* 當(dāng)前是否選中
*/
private int choose = -1;
/**
* 字母畫(huà)筆
*/
private Paint paint = new Paint();
/**
* 顯示的TextView
*/
private TextView mTextDialog;
/**
* 普通時(shí)的顏色
*/
private int normalColor;
/**
* 選中的顏色
*/
private int chooseColor;
/**
* 普通時(shí)的背景
*/
private Drawable normalBackground;
/**
* 選中時(shí)的背景
*/
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設(shè)置顯示字母的TextView
*
* @param mTextDialog
*/
public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}
/**
* 設(shè)置
*
* @param letter
*/
public void setLetter(String[] letter) {
this.letterStrs = letter;
invalidate();
requestLayout();
}
/**
* 初始化參數(shù)
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void init() {
paint.setColor(normalColor);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(textSize);
// 獲取單個(gè)繪制的rect,用于獲取單個(gè)繪制項(xiàng)的高度
mRect = new Rect();
paint.getTextBounds("A", 0, "A".length(), mRect);
}
/**
* 繪制
*
* @param canvas
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 獲取焦點(diǎn)改變背景顏色.
int height = getHeight() - getPaddingTop() - getPaddingBottom();// 獲取對(duì)應(yīng)高度
int width = getWidth(); // 獲取對(duì)應(yīng)寬度
int singleHeight = height / letterStrs.length;// 獲取每一個(gè)字母的高度
for (int i = 0; i < letterStrs.length; i++) {
// 選中的狀態(tài)
if (i == choose) {
paint.setColor(chooseColor);
paint.setFakeBoldText(true);
}
// x坐標(biāo)等于中間-字符串寬度的一半.
float xPos = width / 2 - paint.measureText(letterStrs[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(letterStrs[i], xPos, yPos, paint);
paint.reset();// 重置畫(huà)筆
init();
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
// 點(diǎn)擊的y坐標(biāo)
final float y = event.getY();
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
// 獲取當(dāng)前點(diǎn)擊的字母位置,點(diǎn)擊位置的y坐標(biāo)比上總的高度相當(dāng)于點(diǎn)擊的位置比上全部位置(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));
}
// 抬起時(shí)置為-1
choose = -1;
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
// 設(shè)置背景色
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);
}
// 設(shè)置選中的位置為當(dāng)前位置
choose = currChoose;
invalidate();
}
}
break;
}
return true;
}
/**
* 向外公開(kāi)的方法
*
* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
/**
* 回調(diào)接口
*
* @author coder
*/
public interface OnTouchingLetterChangedListener {
void onTouchingLetterChanged(String s);
}
/**
* 測(cè)量
*
* @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);
// 當(dāng)高度為自適應(yīng)時(shí),高度為字母高度*字母數(shù)量*2 即間隔為單位高度
int wrapHeight = letterStrs.length * (mRect.height() * 2);
// 當(dāng)寬度為自適應(yīng)使,寬度為字母寬度*2
int warpWidth = mRect.width() * 2;
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : warpWidth
, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
//wrap_content時(shí)的高度
: wrapHeight);
}
}
很簡(jiǎn)單,只是提取出來(lái)了一些自定義屬性,沒(méi)什么可說(shuō)的,接下來(lái)分析一下如何讓索引欄變得通用,先來(lái)想一下索引欄一般的寫(xiě)法.首先拿到一個(gè)數(shù)據(jù)源,然后對(duì)這個(gè)數(shù)據(jù)源進(jìn)行處理,從數(shù)據(jù)源中提取出首字母當(dāng)做索引(當(dāng)然數(shù)據(jù)源中可能已經(jīng)含有首字母或者索引等字段),有了索引之后,再在適配器中進(jìn)行判斷來(lái)控制是否顯示索引標(biāo)題(我的做法是判斷第一次出現(xiàn)當(dāng)前索引的數(shù)據(jù)源位置和當(dāng)前位置是否相同,如果相同則顯示索引標(biāo)題),處理完索引標(biāo)題的顯示和隱藏,最后就是跟索引欄進(jìn)行綁定(實(shí)現(xiàn)索引欄的回調(diào)方法并做相關(guān)處理).大體步驟就是這樣,接下來(lái)就是找一下處理不同的地方,比對(duì)了一下,發(fā)現(xiàn)問(wèn)題基本都是出現(xiàn)在數(shù)據(jù)格式不同上,有的數(shù)據(jù)的索引字段可能叫Letter,有的可能叫LetterName,這就導(dǎo)致了每次對(duì)這些數(shù)據(jù)進(jìn)行處理時(shí),都要重新寫(xiě)方法或者修改方法,使得這些方法不共用.那怎么解決一下這個(gè)問(wèn)題呢,最開(kāi)始想到的是寫(xiě)一個(gè)抽象類,然后用一個(gè)抽象方法getLetterName()來(lái)約束索引.每個(gè)需要用到索引欄的Bean都去繼承這個(gè)抽象類,重寫(xiě)這個(gè)抽象方法,從而達(dá)到統(tǒng)一約束索引值的效果,也就解決了索引值字段不同的問(wèn)題,這樣就可以用一個(gè)公共的方法來(lái)處理不同的數(shù)據(jù)源.后來(lái)又考慮了一下,這個(gè)地方其實(shí)用接口會(huì)更加合適一點(diǎn),接口靈活性更大,而且也是面向接口編程的一種體現(xiàn).分析了這么多,來(lái)具體代碼實(shí)現(xiàn)一下,提出一個(gè)接口,之后所有需要用到索引的數(shù)據(jù)Bean去實(shí)現(xiàn)這個(gè)接口中的getLetterName()方法并且重寫(xiě)這個(gè)方法來(lái)返回索引值即可.
接口SideBase:
package com.example.junweiliu.commindexdemo.bean;
/**
* Created by junweiliu on 16/11/21.
*/
public interface SideBase {
String getLetterName();
}
然后數(shù)據(jù)Bean去實(shí)現(xiàn)這個(gè)接口,例如比較常見(jiàn)的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()方法中去返回索引值.
接下來(lái)就可以去寫(xiě)一些公共的處理方法.比如
/**
* 根據(jù)當(dāng)前選中的項(xiàng)獲取其第一次出現(xiàn)該項(xiàng)首字母的位置
*
* @param position 當(dāng)前選中的位置
* @param datas 數(shù)據(jù)源
* @return
*/
public static int getPositionForSection(int position, List<? extends SideBase> datas) {
// 當(dāng)前選中的項(xiàng)
SideBase sideBase = datas.get(position);
for (int i = 0; i < datas.size(); i++) {
String firstStr = datas.get(i).getLetterName().toUpperCase();
// 返回第一次出現(xiàn)該項(xiàng)首字母的位置
if (firstStr.equals(sideBase.getLetterName())) {
return i;
}
}
return -1;
}
因?yàn)槭褂玫慕涌?這里就可以用通配符?的方式來(lái)對(duì)數(shù)據(jù)進(jìn)行處理,只關(guān)心和處理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;
}
/**
* 篩選出數(shù)據(jù)源中所包含的全部索引值
*
* @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()]);
}
通過(guò)分析和重構(gòu)之后,這些之前不能公用的方法就變得通用起來(lái),很方便,之后用起來(lái)也會(huì)特別簡(jiǎn)單、舒心.
完整代碼
公共處理類
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 {
/**
* 根據(jù)當(dāng)前選中的項(xiàng)獲取其第一次出現(xiàn)該項(xiàng)首字母的位置
*
* @param position 當(dāng)前選中的位置
* @param datas 數(shù)據(jù)源
* @return
*/
public static int getPositionForSection(int position, List<? extends SideBase> datas) {
// 當(dāng)前選中的項(xiàng)
SideBase sideBase = datas.get(position);
for (int i = 0; i < datas.size(); i++) {
String firstStr = datas.get(i).getLetterName().toUpperCase();
// 返回第一次出現(xiàn)該項(xiàng)首字母的位置
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;
}
/**
* 篩選出數(shù)據(jù)源中所包含的全部索引值
*
* @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;
/**
* 數(shù)據(jù)源
*/
private List<CityBean> cityBeanList;
/**
* 構(gòu)造方法
*
* @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();
}
// 如果當(dāng)前位置為第一次出現(xiàn)該類首字母的位置,則顯示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 {
/**
* 城市列表數(shù)據(jù)
*/
private List<CityBean> cityBeanList = new ArrayList<>();
/**
* 城市l(wèi)v
*/
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();
}
/**
* 初始化數(shù)據(jù)
*/
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);
// 設(shè)置需要顯示的索引欄內(nèi)容
mSideBar.setLetter(CommUtil.getLetters(cityBeanList));
// 設(shè)置需要顯示的提示框
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"/>
<!-- 內(nèi)容 -->
<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>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 淺析Android位置權(quán)限以及數(shù)組尋找索引的坑
- android仿微信聯(lián)系人索引列表功能
- Android ItemDecoration 實(shí)現(xiàn)分組索引列表的示例代碼
- android仿微信通訊錄搜索示例(匹配拼音,字母,索引位置)
- Android 實(shí)現(xiàn)帶字母索引的側(cè)邊欄功能
- Android自定義View實(shí)現(xiàn)通訊錄字母索引(仿微信通訊錄)
- Android手機(jī)聯(lián)系人帶字母索引的快速查找
- Android手機(jī)聯(lián)系人快速索引(手機(jī)通訊錄)
- android將搜索引擎設(shè)置為中國(guó)雅虎無(wú)法搜索問(wèn)題解決方法
- android 左右滑動(dòng)+索引圖標(biāo)實(shí)現(xiàn)方法與代碼
- Android 解決游戲發(fā)行切包資源索引沖突的問(wèn)題
相關(guān)文章
PowerManagerService之喚醒鎖的使用獲取創(chuàng)建示例解析
這篇文章主要為大家介紹了PowerManagerService之喚醒鎖的使用獲取創(chuàng)建示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Android?獲取實(shí)時(shí)網(wǎng)速實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Android?獲取實(shí)時(shí)網(wǎng)速實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android 媒體庫(kù)數(shù)據(jù)更新方法總結(jié)
這篇文章主要介紹了Android 媒體庫(kù)數(shù)據(jù)更新方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04
教你快速實(shí)現(xiàn)Android動(dòng)態(tài)模糊效果
相信大家都發(fā)現(xiàn)了越來(lái)越多的App里面使用了模糊效果,比如雅虎天氣的界面,雖然我并不知道雅虎天氣是怎么做出這種效果的,但是簡(jiǎn)單的模仿一下的話,還是能做到的。下面一起來(lái)學(xué)習(xí)學(xué)習(xí)。2016-08-08
Android之采用execSQL與rawQuery方法完成數(shù)據(jù)的添刪改查操作詳解
本篇文章是對(duì)用execSQL與rawQuery方法完成數(shù)據(jù)的添刪改查操作進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
android studio 3.0 service項(xiàng)目背景音樂(lè)實(shí)現(xiàn)
這篇文章主要介紹了android studio 3.0中service項(xiàng)目實(shí)現(xiàn)插入背景音樂(lè)的方法。2017-11-11
Android9.0 靜默安裝源碼的實(shí)現(xiàn)
這篇文章主要介紹了Android9.0 靜默安裝源碼的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
Android基于OpenCV實(shí)現(xiàn)圖像金字塔
圖像金字塔是圖像中多尺度表達(dá)的一種,最主要用于圖像的分割,是一種以多分辨率來(lái)解釋圖像的有效但概念簡(jiǎn)單的結(jié)構(gòu)。本文講解Android基于OpenCV實(shí)現(xiàn)圖像金字塔的步驟2021-06-06
Android開(kāi)發(fā)案例手冊(cè)Application跳出dialog
這篇文章主要為大家介紹了Android開(kāi)發(fā)案例手冊(cè)Application跳出dialog,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Android 輕松實(shí)現(xiàn)圖片倒影效果實(shí)例代碼
這篇文章主要介紹了Android 輕松實(shí)現(xiàn)圖片倒影效果實(shí)例代碼,有需要的朋友可以參考一下2014-01-01

