版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nzzl54/article/details/52193011
开始之前先来一段我觉得写得很好的话,相信自己才能在开发之路越走越远!
很多大神都说自定义View是进阶的必经之路,那么自然是要学习的。
自定义View的实现方式大概可以分为三种:(View这里不做详细介绍:请看文章)
1.自绘控件:这个View上所展现的内容全部都是我们自己绘制出来的
2.组合控件:将几个系统原生的控件组合到一起(例子:略)
3.继承控件:只需要去继承一个现有的控件,然后在这个控件上增加一些新的功能,就可以形成一个自定义的控件了(例子:自定义PopupWindow--可扩展操作)
这里介绍第一种“自绘控件”,先来个简单的例子了解一下,后面再深入讲解SideBar,效果如下:
1、设置属性在attrs.xml , 定义属性和声明整个样式,控件就叫TestView,可以设置的属性有背景颜色,字体颜色,和文字内容
<declare-styleable name="TestView">
<attr name="mbackgroundColor" format="color"/>
<attr name="mtextColor" format="color"/>
<attr name="mtext" format="string"/>
</declare-styleable>
2、在布局中使用
<com.example.lanzheng.search.TestView
android:id="@+id/test"
android:layout_width="match_parent"
android:layout_height="50dp"
app:mtext="testtesttest"
app:mbackgroundColor="#00ffbf"
app:mtextColor="#3900a4"/>
3、自定义View的编写
package com.example.lanzheng.search;
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.os.Build;
import android.util.AttributeSet;
import android.view.View;
import com.example.lanzheng.wheeldemo.R;
/**
* Created by lan.zheng on 2016/8/18.
*/
public class TestView extends View{
/**
* 文本的颜色
*/
private int mTitleTextColor;
/**
* 文本的背景颜色
*/
private int mTitleTextBackground;
/**
* 文字
*/
private String mTitleText;
//绘制范围
private Paint mPaint;
private Rect mBound;
public TestView(Context context) {
super(context);
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
//获得我们所定义的自定义样式属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TestView);
//获取自定义的属性数量,根据不的属性来赋值
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.TestView_mbackgroundColor:
mTitleTextBackground = array.getColor(R.styleable.TestView_mbackgroundColor, Color.WHITE);//默认背景颜色
break;
case R.styleable.TestView_mtextColor:
// 默认颜色设置为黑色
mTitleTextColor = array.getColor(R.styleable.TestView_mtextColor, Color.BLACK); //默认字体颜色
break;
case R.styleable.TestView_mtext:
mTitleText = array.getString(attr); //获得文字
break;
}
}
array.recycle();
//初始化Paint,为绘画做准备
mPaint = new Paint();
//设置文本边界
mBound = new Rect();
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
}
@Override
protected void onDraw(Canvas canvas)
{
//绘画,背景
mPaint.setColor(mTitleTextBackground);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
//绘画,文字颜色和内容
mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
//重写该方法用于适配宽和高
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height ;
if (widthMode == MeasureSpec.EXACTLY)
{
width = widthSize;
} else
{
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textWidth = mBound.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else
{
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textHeight = mBound.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}
}
接下来对自定义View有了认识,我们来做快速检索的SideBar,效果如下:
1.自定义View的编写,这里直接在View中固定想要的颜色和文字,所以不需要再attrs.xml加入什么
package com.example.lanzheng;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
/**
* 字母条view
* 快速检索
*/
public class LetterSideBar extends View {
// 触摸事件
private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
// 26个字母
public static String[] b = {"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", "#"};
//默认没有选中的,非选中为-1
private int choose = -1;
//画笔,设置
private Paint paint = new Paint();
//选中文字显示View
private TextView mTextDialog;
public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}
public LetterSideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public LetterSideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LetterSideBar(Context context) {
super(context);
}
/**
* 重写这个方法
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();// 获取对应高度
int width = getWidth(); // 获取对应宽度
int singleHeight = height / b.length;// 获取每一个字母的高度
// 获取焦点改变背景颜色.
for (int i = 0; i < b.length; i++) {
// paint.setColor(Color.rgb(3,123,254));
// paint.setColor(Color.WHITE);
paint.setColor(Color.parseColor("#43CD80")); //默认颜色green
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(20);
// 选中的状态
if (i == choose) {
paint.setColor(Color.parseColor("#228B22")); //选中的颜色gray green
paint.setFakeBoldText(true);
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}
}
/**
* 重写触摸事件
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; //拿到实例
final int c = (int) (y / getHeight() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
switch (action) {
case MotionEvent.ACTION_UP:
//setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1; //抬起 = 1 ,MotionEvent.ACTION_UP
invalidate(); //重绘
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
//setBackgroundResource(R.drawable.sidebar_background);
if (oldChoose != c) {
if (c >= 0 && c < b.length) {
if (listener != null) {
listener.onTouchingLetterChanged(b[c]);//回调,返回当前选中的字符
}
if (mTextDialog != null) {
mTextDialog.setText(b[c]);
mTextDialog.setVisibility(View.VISIBLE);
}
choose = c;
invalidate(); //重绘
}
}
break;
}
return true;
}
/**
* 向外公开的方法
* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener)
{
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
/**
* 接口
* @author coder
*/
public interface OnTouchingLetterChangedListener {
public void onTouchingLetterChanged(String s);
}
}
2.布局中使用activity_search.xml
<FrameLayout
android:paddingTop="10dp"
android:id="@+id/layout_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_marginTop="75dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<TextView
android:id="@+id/dialog"
android:layout_width="80.0dip"
android:layout_height="80.0dip"
android:layout_gravity="center"
android:background="@drawable/show_head_toast_bg"
android:gravity="center"
android:textColor="#ffffffff"
android:textSize="30.0dip"
android:visibility="invisible" /><!--用于指示选中字母-->
<com.example.lanzheng.LetterSideBar
android:id="@+id/sidrbar"
android:layout_width="35dp"
android:layout_height="match_parent"
android:layout_gravity="right|center" />
</FrameLayout>
3.Activity中进行初始化和监听
private TextView letterTextView;
private LetterSideBar mLetterSideBar;
private RecyclerView mRecyclerView;
private void initView(){
letterTextView = (TextView)findViewById(R.id.dialog) ;
mLetterSideBar = (LetterSideBar) findViewById(R.id.sidrbar);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mLetterSideBar.setTextView(letterTextView); //设置显示选中显示的View
mLetterSideBar.setOnTouchingLetterChangedListener(new LetterSideBar.OnTouchingLetterChangedListener() {
@Override
public void onTouchingLetterChanged(String s) {
// 该字母首次出现的位置
Log.d("test","s = "+s);
int position = mListAdapter.getPositionForSection(s.charAt(0)); //在Adapter中去获得要移动到的位置,Adatper内容请自定义
if (position != -1) {
//mIndex = position; //获得position
//moveToPosition(lLinearLayoutManager); //滑动效果,此处暂时不用,需要配合addOnScrollListener使用
lLinearLayoutManager.scrollToPositionWithOffset(position,0); //无效果的滑动
}
}
});
}
感觉这个有点简单了是吧,要显示的数组是定死的,那我们就改改代码吧,这里默认没有给数据的时候显示默认为居中的“空”字,当字符小于20,就不给他那么大的空间,小于21个搜索字符时就居中,并且增加了点击后背景的效果,效果如下所示:
代码如下:
/**
* Created by lan.zheng on 2016/9/23.
*/
public class LetterSideBar extends View {
// 触摸事件
private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
public static String[] b = {"空"}; //默认给一个,防止抛出异常
public int stringLength = 1; //数组长度
public static int itemHeight = 50; //小于21个时候的高度定死
public static int itemNumAbove = 20; //用于超出20判断
int beginPosition = 0; //开始位置
int totalHeight = 0; //搜索有字的高度
//默认没有选中的,非选中为-1
private int choose = -1;
//画笔,设置
private Paint paint = new Paint();
//选中文字显示View
private TextView mTextDialog;
public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}
public void setStrings(String[] strings){
b = strings;
stringLength = b.length;
if(stringLength > itemNumAbove){
totalHeight = getHeight();
}else {
totalHeight = itemHeight;
}
}
public LetterSideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public LetterSideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LetterSideBar(Context context) {
super(context);
}
/**
* 重写这个方法
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();// 获取对应高度
int width = getWidth(); // 获取对应宽度
int singleHeight;
if(stringLength > itemNumAbove){
singleHeight = height / stringLength;// 获取每一个字母的高度
}else {
singleHeight = itemHeight;
beginPosition = height/2 - singleHeight*stringLength/2;
}
// 获取焦点改变背景颜色.
for (int i = 0; i < stringLength; i++) {
// paint.setColor(Color.rgb(3,123,254));
// paint.setColor(Color.WHITE);
paint.setColor(Color.parseColor("#43CD80")); //默认颜色green
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
paint.setTextSize(20);
// 选中的状态
if (i == choose) {
paint.setColor(Color.parseColor("#228B22")); //选中的颜色gray green
paint.setFakeBoldText(true);
}
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = beginPosition + singleHeight*i + singleHeight/2;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}
}
/**
* 重写触摸事件
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
if(y < beginPosition || y > getHeight() - beginPosition){ //非点击位置的不做处理
setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1;
invalidate(); //重绘
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
}else {
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; //拿到实例
// 获取点击了第几个
// final int c2 = (int) (y / getHeight() * b.length);
int sigleItemHight = itemHeight;
if(stringLength > itemNumAbove){
sigleItemHight = getHeight()/stringLength;
}
final int c = (int) ((y - beginPosition)/sigleItemHight); //点击的位置-开始的位置,得到了实际移动的位置,总位置除以它就能得到点击的字母是什么
switch (action) {
case MotionEvent.ACTION_UP:
setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1; //抬起 = 1 ,MotionEvent.ACTION_UP
invalidate(); //重绘
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
setBackgroundDrawable(new ColorDrawable(0x20000000));
if (oldChoose != c) {
if (c >= 0 && c < b.length) {
if (listener != null) {
listener.onTouchingLetterChanged(b[c]);//回调,返回当前选中的字符
}
if (mTextDialog != null) {
mTextDialog.setText(b[c]);
mTextDialog.setVisibility(View.VISIBLE);
}
choose = c;
invalidate(); //重绘
}
}
break;
}
}
return true;
}
/**
* 向外公开的方法
* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener)
{
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
/**
* 接口
* @author coder
*/
public interface OnTouchingLetterChangedListener {
public void onTouchingLetterChanged(String s);
}
}
使用时如下即可:
letterSideBar = (LetterSideBar)findViewById(R.id.letter);
letterSideBar.setStrings(s2);
以上就是比较简单的搜索了,更复杂的效果可以自己改代码,这里不多说了。