说明
很多应用中我们都能看到关于侧边栏的使用,比如微信啊,QQ啊,美团啊等等,最常见的应该就是通讯录里面对联系人进行A~Z的排序。侧边栏主要是方便用户进行字母索引。资料链接:http://blog.csdn.net/xiaanming/article/details/12684155
今天实现的控件效果如下图所示:
工程结构图:
1.Sidebar的JAVA类文件
package com.example.mysidebar;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import org.w3c.dom.Attr;
/**
* Created by Administrator on 2016/7/7 0007.
*/
public class SideBar extends View {
public SideBar(Context context) {
super(context);
}
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 触摸字母索引发生变化的回调接口
*/
private onLetterTouchedChangeListener onLetterTouchedChangeListener = null;
//侧边栏字母显示
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", "#"
};
//变量 currentChoosenAlphabetIndex 用来标示当前手指触摸的字母索引在 alphabet 数组中的下标
private int currentChoosenAlphabetIndex = -1;
//定义画笔
private Paint paint = new Paint();
//当手指在 SideBar 上滑动的时候,会有一个 TextView 来显示当前手指触摸的字母索引,所以还需要一个属性
private TextView textViewDialog = null;
/**
* 为SideBar设置显示字母的TextView
* @param textViewDialog
*/
public void setTextViewDialog(TextView textViewDialog) {
this.textViewDialog = textViewDialog;
}
/**
* 绘制列表控件的方法
* 将要绘制的字母以从上到下的顺序绘制在一个指定区域
* 如果是进行选中的字母就进行高亮显示
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//获取SideBar的高度
int viewHeight = getHeight();
//获取SideBar的宽度
int viewWidth = getWidth();
//获得每个字母索引的高度
int singleHeight = viewHeight / alphabet.length;
//绘制每一个字母的索引
for (int i = 0; i < alphabet.length; i++) {
paint.setColor(Color.rgb(34, 66, 99));//设置字母颜色
paint.setTypeface(Typeface.DEFAULT_BOLD);//设置字体
paint.setTextSize(20);//设置字体大小
paint.setAntiAlias(true);//抗锯齿
//如果当前的手指触摸索引和字母索引相同,那么字体颜色进行区分
if (currentChoosenAlphabetIndex == i) {
paint.setColor(Color.parseColor("#3399ff"));
paint.setFakeBoldText(true);
}
/*
* 绘制字体,需要制定绘制的x、y轴坐标
*
* x轴坐标 = 控件宽度的一半 - 字体宽度的一半
* y轴坐标 = singleHeight * i + singleHeight
*/
float xpos = viewWidth / 2 - paint.measureText(alphabet[i]) / 2;
float ypos = singleHeight * i + singleHeight;
canvas.drawText(alphabet[i], xpos, ypos, paint);
// 重置画笔,准备绘制下一个字母索引
paint.reset();
}
}
public void setOnLetterTouchedChangeListener(
onLetterTouchedChangeListener onLetterTouchedChangeListener) {
this.onLetterTouchedChangeListener = onLetterTouchedChangeListener;
}
private onLetterTouchedChangeListener getOnLetterTouchedChangeListener() {
return onLetterTouchedChangeListener;
}
/**
* 当手指触摸的字母索引发生变化时,调用该回调接口
*
* @author owen
*/
public interface onLetterTouchedChangeListener {
public void onTouchedLetterChange(String letterTouched);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// 触摸事件的代码
final int action = event.getAction();
//手指触摸点在屏幕的Y坐标
final float touchYPos = event.getY();
// 因为currentChoosenAlphabetIndex会不断发生变化,所以用一个变量存储起来
int preChoosenAlphabetIndex = currentChoosenAlphabetIndex;
final onLetterTouchedChangeListener listener = getOnLetterTouchedChangeListener();
// 比例 = 手指触摸点在屏幕的y轴坐标 / SideBar的高度
// 触摸点的索引 = 比例 * 字母索引数组的长度
final int currentTouchIndex = (int) (touchYPos / getHeight() * alphabet.length);
switch (action) {
case MotionEvent.ACTION_UP:
// 如果手指没有触摸屏幕,SideBar的背景颜色为默认,索引字母提示控件不可见
setBackground(new ColorDrawable(0x00000000));
currentChoosenAlphabetIndex = -1;
invalidate();
if (textViewDialog != null) {
textViewDialog.setVisibility(View.INVISIBLE);
}
break;
default:
// 其他情况,比如滑动屏幕、点击屏幕等等,SideBar会改变背景颜色,索引字母提示控件可见,同时需要设置内容
setBackgroundResource(R.drawable.sidebar_background);
// 不是同一个字母索引
if (currentTouchIndex != preChoosenAlphabetIndex) {
// 如果触摸点没有超出控件范围
if (currentTouchIndex >= 0 && currentTouchIndex < alphabet.length) {
if (listener != null) {
listener.onTouchedLetterChange(alphabet[currentTouchIndex]);
}
if (textViewDialog != null) {
textViewDialog.setText(alphabet[currentTouchIndex]);
textViewDialog.setVisibility(View.VISIBLE);
}
currentChoosenAlphabetIndex = currentTouchIndex;
invalidate();
}
}
break;
}
return super.dispatchTouchEvent(event);
}
}
SideBar类就是ListView右侧的字母索引View,我们需要使用setTextView(TextView textViewDialog)来设置用来显示当前按下的字母的TextView,以及使用setOnLetterTouchedChangeListener方法来设置回调接口,在回调方法onLetterTouchedChangeListener(String s)中来处理不同的操作
2.引用SideBar的XML文件(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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.mysidebar.MainActivity">
<TextView
android:id="@+id/textViewDialog"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:background="#3399ff"
android:gravity="center"
android:padding="10dp"
android:text="1"
android:textColor="@android:color/white"
android:textSize="50dp"
android:textStyle="bold"
android:visibility="invisible" >
</TextView>
<com.example.mysidebar.SideBar
android:id="@+id/sideBar"
android:layout_width="25dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true" />
</RelativeLayout>
背景自定义
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#99C60000"
android:endColor="#99C60000"
android:angle="90.0" />
<corners
android:topLeftRadius="8dip"
android:bottomLeftRadius="8dip"/>
</shape>
shape是用来定义形状的,gradient定义该形状里面为渐变色填充,startColor起始颜色,endColor结束颜色,angle表示方向角度。当angle=0时,渐变色是从左向右。 然后逆时针方向转,当angle=90时为从下往上
corner是用来定义圆角的,radius为角的弧度,值越大角越圆。
我们还可以把四个角设定成不同的角度,
同时设置五个属性,则Radius属性无效
android:Radius=”20dp” 设置四个角的半径
android:topLeftRadius=”20dp” 设置左上角的半径
android:topRightRadius=”20dp” 设置右上角的半径
android:bottomLeftRadius=”20dp” 设置右下角的半径
android:bottomRightRadius=”20dp” 设置左下角的半径
3.MainActivity的JAVA类文件
package com.example.mysidebar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private SideBar indexBar;
/**
* 显示字母的TextView
*/
private TextView textViewDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
indexBar = (SideBar) findViewById(R.id.sideBar);
textViewDialog = (TextView) findViewById(R.id.textViewDialog);
indexBar.setTextViewDialog(textViewDialog);
}
}
以上就是全部代码了。希望自己能多多练习更加熟练的掌握。