:)
本文主要是介绍了一个自定义控件的实现
- 首先来看demo效果
- 这里有几个问题需要注意
- 控件的 高度 怎么确定
- 字体问题每个字符的 字体 都不一样
- 字体 颜色 问题,当前选中的字符颜色是不一样的
- 怎么判断当前是 选中 的那个字符
选择实现方案,是继承View还是ViewGroup
- 继承ViewGroup(比如:LinearLayout),好处就是比较方便实现,不是太好的就是效率不高(要实例化多个子控件)
- 继承View,好处就是效率较高,自己比较好控制,不爽的就是比较麻烦。
这个demo就选择继承View吧
- 接下来一个一个解决上面提到的问题(字体,颜色,高度,滑动的位置判断)
总体思路
首先要明白的一点是滑动的监听肯定是通过重写onTouchEvent方法来实现的。这里就还有一个问题,onTouchEvent方法我们只能得到坐标信息怎么通过坐标信息转变成当前是滑动到那个字符?demo采用的解决方法是将每个字符对应的坐标都保存到一个实体里面,判断位置的时候就根据每个字符的坐标信息就可以判断是滑动到那个实体。
字体大小怎么确定?demo里面规定了一个最大的字体和最小的字体,当前选中的字符使用最大的字体,其他字符就根据它离最大字体的位置来设置字体大小
控件的高度的确定?其实就是当选中中间的字符的时候的高度
下面有大量代码
[↓][↓][↓]
[↓][↓][↓]
[↓][↓][↓]
[↓][↓][↓][↓][↓][↓][↓]
[↓][↓][↓]
[↓]
定义控件会用到的属性
/**
*
* 最大的字体
*
*/
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;
/**
*
* 默认的字体颜色
*
*/
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;
/**
*
* 默认的选中位置
*
*
*/
private int currentChoise = 0;
建立一个实体类来保存每个字符的位置信息(在滑动和绘制的时候判断位置)
class TextSizeModel {
private int x;
private int y;
private int textSize;
private String item;
}
- 会为每个字符都生成一个这样的实体,实体里面的textSize保存当前字符的字体大小,当选中位置改变之后就可以更新这个实体类,最后在绘制的时候根据这个实体类的信息来进行绘制就可以了
控件高度的确定(这个肯定就是重写 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 这个主要是当选中中间位置的时候控件的高度是最高的
高度的计算方法其实也比较简单,就是看当前字符距离当前选中字符的个数,textSize = 最大的字体 - 当前的距离 * 字体偏移量
这里还有一个要注意的是 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;
}
}
- 这里其实就是便利实体类集合,根据onMesure里面测量的字体大小就行字符的绘制就可以了
- 还有一个需要注意的是这里进行的实体的坐标的设置
滑动的监听(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的时候进行坐标判断,根据上一次绘制的时候实体的坐标和当前坐标更新选中的位置,最后initItemTextSize重新根据当前的选中位置更新了每个字符的字体大小,之后绘制
源码
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;
/**
*
* 默认的字体颜色
*
*/
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;
/**
*
* 默认的选中位置
*
*
*/
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