自定義View的嘗試-A到Z快速搜尋sideBar
阿新 • • 發佈:2018-12-12
開始之前先來一段我覺得寫得很好的話,相信自己才能在開發之路越走越遠!
很多大神都說自定義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);
以上就是比較簡單的搜尋了,更復雜的效果可以自己改程式碼,這裡不多說了。