:)
本文主要是介紹了一個自定義控件的實現(xiàn)
- 首先來看demo效果
- 這里有幾個問題需要注意
- 控件的 高度 怎么確定
- 字體問題每個字符的 字體 都不一樣
- 字體 顏色 問題,當(dāng)前選中的字符顏色是不一樣的
- 怎么判斷當(dāng)前是 選中 的那個字符
選擇實現(xiàn)方案句狼,是繼承View還是ViewGroup
- 繼承ViewGroup(比如:LinearLayout)嗜浮,好處就是比較方便實現(xiàn)李滴,不是太好的就是效率不高(要實例化多個子控件)
- 繼承View荧琼,好處就是效率較高,自己比較好控制充甚,不爽的就是比較麻煩演熟。
這個demo就選擇繼承View吧
- 接下來一個一個解決上面提到的問題(字體鞭执,顏色,高度芒粹,滑動的位置判斷)
總體思路
首先要明白的一點是滑動的監(jiān)聽肯定是通過重寫onTouchEvent方法來實現(xiàn)的兄纺。這里就還有一個問題,onTouchEvent方法我們只能得到坐標(biāo)信息怎么通過坐標(biāo)信息轉(zhuǎn)變成當(dāng)前是滑動到那個字符化漆?demo采用的解決方法是將每個字符對應(yīng)的坐標(biāo)都保存到一個實體里面估脆,判斷位置的時候就根據(jù)每個字符的坐標(biāo)信息就可以判斷是滑動到那個實體。
字體大小怎么確定座云?demo里面規(guī)定了一個最大的字體和最小的字體疙赠,當(dāng)前選中的字符使用最大的字體付材,其他字符就根據(jù)它離最大字體的位置來設(shè)置字體大小
控件的高度的確定?其實就是當(dāng)選中中間的字符的時候的高度
下面有大量代碼
[↓][↓][↓]
[↓][↓][↓]
[↓][↓][↓]
[↓][↓][↓][↓][↓][↓][↓]
[↓][↓][↓]
[↓]
定義控件會用到的屬性
/**
*
* 最大的字體
*
*/
private int maxTextSize = DEFAULT_MAX_TEXT_SIZE;
/**
*
* 最小的字體
*
*/
private int minTextSize = DEFAULT_MIN_TEXT_SIZE;
/**
*
* 不同字體之間的間隔
*
*/
private int textSizeStep = DEFAULT_TEXT_STEP;
/**
*
* 選中的時候字體顏色
*
*/
private int choiseTextColor = DEFAULT_CHOISE_COLOR;
/**
*
* 默認(rèn)的字體顏色
*
*/
private int defaultTextColor = DEFAULT_COLOR;
/**
*
* 字符上下間距
*
*
*/
private int margin = 8;
private static final int DEFAULT_MAX_TEXT_SIZE = 100;//px
private static final int DEFAULT_MIN_TEXT_SIZE = 15;
private static final int DEFAULT_TEXT_STEP = 10;
private static final int DEFAULT_CHOISE_COLOR = Color.BLACK;
private static final int DEFAULT_COLOR = Color.GRAY;
/**
*
* 默認(rèn)的選中位置
*
*
*/
private int currentChoise = 0;
建立一個實體類來保存每個字符的位置信息(在滑動和繪制的時候判斷位置)
class TextSizeModel {
private int x;
private int y;
private int textSize;
private String item;
}
- 會為每個字符都生成一個這樣的實體圃阳,實體里面的textSize保存當(dāng)前字符的字體大小厌衔,當(dāng)選中位置改變之后就可以更新這個實體類,最后在繪制的時候根據(jù)這個實體類的信息來進(jìn)行繪制就可以了
控件高度的確定(這個肯定就是重寫 onMeasure了)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = maxTextSize + margin * 2;
int height = margin * 2;
if(datas == null || datas.size() == 0){
setMeasuredDimension(width,height);
return;
}
currentChoise = datas.size() / 2;
//height += initItemTextSize();
for(int i = 0;i < datas.size();i++){
int duration = i < currentChoise ? currentChoise - i : i - currentChoise;
int textSize = maxTextSize - duration * textSizeStep;
textSize = textSize < minTextSize ? minTextSize : textSize;
datas.get(i).setTextSize(textSize);
height += textSize + margin;
}
setMeasuredDimension(width,height);
}
這里的datas就是所有字符的實體類
currentChoise = datas.size() / 2 這個主要是當(dāng)選中中間位置的時候控件的高度是最高的
高度的計算方法其實也比較簡單捍岳,就是看當(dāng)前字符距離當(dāng)前選中字符的個數(shù)富寿,textSize = 最大的字體 - 當(dāng)前的距離 * 字體偏移量
這里還有一個要注意的是 onMeasure 里面初始化了字體的大小
有了字體大小,接下來就是繪制了(onDraw)
@Override
protected void onDraw(Canvas canvas) {
int currentY = margin + maxTextSize;
for(int i = 0;i < datas.size();i++){
paint.setColor(i == currentChoise ? choiseTextColor : defaultTextColor);
paint.setTextSize(datas.get(i).getTextSize());
canvas.drawText(datas.get(i).getItem(),
getMeasuredWidth() / 2,currentY,paint);
datas.get(i).setX(0);
datas.get(i).setY(currentY);
currentY += datas.get(i).getTextSize() + margin;
}
}
- 這里其實就是便利實體類集合锣夹,根據(jù)onMesure里面測量的字體大小就行字符的繪制就可以了
- 還有一個需要注意的是這里進(jìn)行的實體的坐標(biāo)的設(shè)置
滑動的監(jiān)聽(onTouchEvent)
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
for(int i=0;i < datas.size();i++){
TextSizeModel model = datas.get(i);
if(model.getY() < y && model.getY() + margin > y){
currentChoise = i;
break;
}
}
initItemTextSize();
postInvalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
- 主要就是在ACTION_MOVE的時候進(jìn)行坐標(biāo)判斷页徐,根據(jù)上一次繪制的時候?qū)嶓w的坐標(biāo)和當(dāng)前坐標(biāo)更新選中的位置,最后initItemTextSize重新根據(jù)當(dāng)前的選中位置更新了每個字符的字體大小银萍,之后繪制
源碼
package com.suse.yuxin.emptydemo.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.suse.yuxin.emptydemo.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2017/4/29.
*/
public class LettersView extends View{
/**
*
* 最大的字體
*
*/
private int maxTextSize = DEFAULT_MAX_TEXT_SIZE;
/**
*
* 最小的字體
*
*/
private int minTextSize = DEFAULT_MIN_TEXT_SIZE;
/**
*
* 不同字體之間的間隔
*
*/
private int textSizeStep = DEFAULT_TEXT_STEP;
/**
*
* 選中的時候字體顏色
*
*/
private int choiseTextColor = DEFAULT_CHOISE_COLOR;
/**
*
* 默認(rèn)的字體顏色
*
*/
private int defaultTextColor = DEFAULT_COLOR;
/**
*
* 字符上下間距
*
*
*/
private int margin = 8;
private static final int DEFAULT_MAX_TEXT_SIZE = 100;//px
private static final int DEFAULT_MIN_TEXT_SIZE = 15;
private static final int DEFAULT_TEXT_STEP = 10;
private static final int DEFAULT_CHOISE_COLOR = Color.BLACK;
private static final int DEFAULT_COLOR = Color.GRAY;
/**
*
* 默認(rèn)的選中位置
*
*
*/
private int currentChoise = 0;
private Paint paint;
private List<TextSizeModel> datas = new ArrayList<>();
private static final String LOG_TAG = "LettersView_TAG";
public LettersView(Context context) {
super(context);
init(context,null);
}
public LettersView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
public LettersView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context,attrs);
}
private void init(Context context,AttributeSet attrs) {
if(attrs == null){
return;
}
//init attr
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.LettersView);
maxTextSize = (int) arr.getDimension(R.styleable.LettersView_maxTextSize,DEFAULT_MAX_TEXT_SIZE);
minTextSize = (int) arr.getDimension(R.styleable.LettersView_minTextSize,DEFAULT_MIN_TEXT_SIZE);
textSizeStep = (int) arr.getDimension(R.styleable.LettersView_textStep,DEFAULT_TEXT_STEP);
choiseTextColor = arr.getColor(R.styleable.LettersView_choiseTextColor,DEFAULT_CHOISE_COLOR);
defaultTextColor = arr.getColor(R.styleable.LettersView_defaultTextColor,DEFAULT_COLOR);
arr.recycle();
Log.i(LOG_TAG,"maxT "+maxTextSize+" minT"+minTextSize+" step "+textSizeStep);
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = maxTextSize + margin * 2;
int height = margin * 2;
if(datas == null || datas.size() == 0){
setMeasuredDimension(width,height);
return;
}
currentChoise = datas.size() / 2;
//height += initItemTextSize();
for(int i = 0;i < datas.size();i++){
int duration = i < currentChoise ? currentChoise - i : i - currentChoise;
int textSize = maxTextSize - duration * textSizeStep;
textSize = textSize < minTextSize ? minTextSize : textSize;
datas.get(i).setTextSize(textSize);
height += textSize + margin;
}
setMeasuredDimension(width,height);
}
private int initItemTextSize(){
int height = 0;
for(int i = 0;i < datas.size();i++){
int duration = i < currentChoise ? currentChoise - i : i - currentChoise;
int textSize = maxTextSize - duration * textSizeStep;
textSize = textSize < minTextSize ? minTextSize : textSize;
datas.get(i).setTextSize(textSize);
height += textSize + margin;
}
return height;
}
@Override
protected void onDraw(Canvas canvas) {
int currentY = margin + maxTextSize;
for(int i = 0;i < datas.size();i++){
paint.setColor(i == currentChoise ? choiseTextColor : defaultTextColor);
paint.setTextSize(datas.get(i).getTextSize());
canvas.drawText(datas.get(i).getItem(),
getMeasuredWidth() / 2,currentY,paint);
datas.get(i).setX(0);
datas.get(i).setY(currentY);
currentY += datas.get(i).getTextSize() + margin;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
for(int i=0;i < datas.size();i++){
TextSizeModel model = datas.get(i);
if(model.getY() < y && model.getY() + margin > y){
currentChoise = i;
break;
}
}
initItemTextSize();
postInvalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
public void setData(List<String> letters){
if(letters == null || letters.size() == 0){
return;
}
datas.clear();
for (String letter : letters) {
TextSizeModel sizeModel = new TextSizeModel();
sizeModel.setItem(letter);
datas.add(sizeModel);
}
requestLayout();
}
public void setData(String[] letters){
if(letters == null || letters.length == 0){
return;
}
List<String> l = new ArrayList<>();
for(int i=0;i < letters.length;i++){
l.add(letters[i]);
}
setData(l);
}
class TextSizeModel {
private int x;
private int y;
private int textSize;
private String item;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getTextSize() {
return textSize;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
}
<declare-styleable name="LettersView">
<attr name="maxTextSize" format="dimension"/>
<attr name="minTextSize" format="dimension"/>
<attr name="choiseTextColor" format="color"/>
<attr name="defaultTextColor" format="color"/>
<attr name="textStep" format="color"></attr>
</declare-styleable>
如何使用
public class LettersTestActivity extends AppCompatActivity{
LettersView lettersView;
String[] letters = new String[]{
"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"
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_letters_test);
lettersView = (LettersView) findViewById(R.id.activity_letters_test_content);
lettersView.setData(letters);
}
}
Nothing is certain in this life. The only thing i know for sure is that. I love you and my life. That is the only thing i know. have a good day