android仿ios版本飛常準app字母列表索引,純原創。
阿新 • • 發佈:2019-02-13
仿飛常準字母索引,純原創。
偶然看見飛常準(ios)的字母列表索引,覺得很酷炫,ios可以的android沒有不行的,所以就寫了一個demo,實現效果完全一樣。
自定義字母索引view
package com.example.sunsh.letter;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Created by sunsh on 2017/8/7.
*/
public class LetterIndext extends View {
private Paint paint ;
private float TEXT_WIDTH = 60;
private float MAX_DISTANCE = 140;
private final float NORMAL_SIZE = 26;
private float MAX_SIZE = 80;
private String DEFAULT_COLOR = "606060";
private int SELECT_COLOR = Color.WHITE;
//字母容器
private ArrayList<LetterData> list = new ArrayList <>();
private int width;
private int height;
private int text_height;
//拋物線係數
private float ratio = 0.005f;
private int position;
public LetterIndext(Context context) {
super(context);
initPaint();
}
public LetterIndext(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public LetterIndext(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
public void setList(List<String> list) {
this.list.clear();
for (int i = 0; i < list.size(); i++) {
//預設距離移動距離0,預設大小40,預設顏色灰色cdcdcd,預設不加粗
this.list.add(new LetterData(list.get(i), 0, NORMAL_SIZE, Color.parseColor("#"+DEFAULT_COLOR), false));
}
invalidate();
}
public void setSelectColor(int color){
this.SELECT_COLOR = color;
}
private void initPaint() {
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
width = canvas.getWidth();
height = canvas.getHeight();
canvas.translate(width - TEXT_WIDTH, 0);
if (list.size() > 0) {
text_height = height / list.size();
for (int i = 0; i < list.size(); i++) {
if (i != position) {
LetterData letterData = list.get(i);
paint.setColor(Color.TRANSPARENT);
Rect rect = new Rect(0, i * text_height, (int) TEXT_WIDTH, i * text_height + text_height);
canvas.drawRect(rect, paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
int baseline = (int) ((rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2);
paint.setTextSize(letterData.getTextSize());
paint.setColor(letterData.getTextColor());
if (letterData.isBlod)
paint.setFakeBoldText(true);
else paint.setFakeBoldText(false);
canvas.drawText(letterData.getText(), 30 - letterData.distance, baseline, paint);
}
}
if (position < list.size() && position >= 0) {
LetterData letterData = list.get(position);
paint.setColor(Color.TRANSPARENT);
Rect rect = new Rect(0, position * text_height, (int) TEXT_WIDTH, position * text_height + text_height);
canvas.drawRect(rect, paint);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
int baseline = (int) ((rect.bottom + rect.top - fontMetrics.bottom - fontMetrics.top) / 2);
paint.setTextSize(letterData.getTextSize());
paint.setColor(letterData.getTextColor());
if (letterData.isBlod)
paint.setFakeBoldText(true);
else paint.setFakeBoldText(false);
canvas.drawText(letterData.getText(), 30 - letterData.distance, baseline, paint);
}
}
}
private boolean isAnimation = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if (event.getPointerCount() == 1) {
switch (action) {
case MotionEvent.ACTION_DOWN:
float x = event.getX();
float y = event.getY();
if (x > width - TEXT_WIDTH) {
if (text_height != 0) {
position = (int) (y / text_height);
if (position >= 0 && position < list.size())
if (!isAnimation) {
isAnimation = true;
onActionDown(position, y);
}
}
return true;
}
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
if (text_height != 0) {
position = (int) (moveY / text_height);
if (position >= 0 && position < list.size())
onActionMove(position, moveY);
else {
if (position < 0) position = 0;
if (position >= list.size()) position = list.size() - 1;
onActionUp();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (text_height != 0) {
int upPosition = (int) (event.getY() / text_height);
if (upPosition >= 0 && upPosition < list.size()) {
isAnimation = true;
if (onSeletListener!=null){
onSeletListener.onSelect(upPosition,list.get(upPosition).getText());
}
onActionUp();
return true;
}
}
break;
default:
isAnimation = true;
onActionUp();
break;
}
}
return super.onTouchEvent(event);
}
private void onActionUp() {
for (int i = position - 4; i <= position + 4; i++) {
if (i >= 0 && i < list.size()) {
final LetterData letterData = list.get(i);
final float distance = letterData.getDistance();
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(distance, 0);
if (i == position) {
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
float scaleSize = animatedValue / MAX_DISTANCE;
float offsetSize = (MAX_SIZE - NORMAL_SIZE) * scaleSize;
int alpha = (int) (255 * (1 - (animatedValue / MAX_DISTANCE)));
String str_alpha = Integer.toHexString(alpha);
int length = str_alpha.length();
if (length < 2) {
str_alpha = "0" + str_alpha;
}
if (animatedValue < 70) {
letterData.setState(animatedValue, NORMAL_SIZE + offsetSize, Color.parseColor("#" + str_alpha + DEFAULT_COLOR), false);
} else {
letterData.setState(animatedValue, NORMAL_SIZE + offsetSize, SELECT_COLOR, false);
}
invalidate();
}
});
valueAnimator.setDuration(100);
valueAnimator.start();
} else {
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
float scaleSize = animatedValue / MAX_DISTANCE;
float offsetSize = (MAX_SIZE - NORMAL_SIZE) * scaleSize;
int alpha = (int) (255 * (1 - (animatedValue / MAX_DISTANCE)));
String str_alpha = Integer.toHexString(alpha);
int length = str_alpha.length();
if (length < 2) {
str_alpha = "0" + str_alpha;
}
letterData.setState(animatedValue, NORMAL_SIZE + offsetSize, Color.parseColor("#" + str_alpha + DEFAULT_COLOR), false);
invalidate();
}
});
valueAnimator.setDuration(100);
valueAnimator.start();
}
if (i == position + 4 || i == list.size() - 1) {
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
isAnimation = false;
animator.removeAllListeners();
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
}
}
}
}
private void onActionDown(final int indext, float downY) {
for (int i = 0; i < list.size(); i++) {
if (i >= indext - 4 && i <= indext + 4) {
float distance = -ratio * (downY - getCenterY(i)) * (downY - getCenterY(i)) + MAX_DISTANCE;
if (distance < 0) distance = 0;
final LetterData data = list.get(i);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, distance);
if (i != indext) {
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
float scaleSize = animatedValue / MAX_DISTANCE;
float offsetSize = (MAX_SIZE - NORMAL_SIZE) * scaleSize;
int alpha = (int) (255 * (1 - (animatedValue / MAX_DISTANCE)));
String str_alpha = Integer.toHexString(alpha);
int length = str_alpha.length();
if (length < 2) {
str_alpha = "0" + str_alpha;
}
data.setState(animatedValue, NORMAL_SIZE + offsetSize, Color.parseColor("#" + str_alpha + DEFAULT_COLOR), false);
invalidate();
}
});
valueAnimator.setDuration(100);
valueAnimator.start();
} else {
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float animatedValue = (float) valueAnimator.getAnimatedValue();
float scaleSize = animatedValue / MAX_DISTANCE;
float offsetSize = (MAX_SIZE - NORMAL_SIZE) * scaleSize;
int alpha = (int) (255 * (1 - (animatedValue / MAX_DISTANCE)));
String str_alpha = Integer.toHexString(alpha);
int length = str_alpha.length();
if (length < 2) {
str_alpha = "0" + str_alpha;
}
if (animatedValue > 70)
data.setState(animatedValue, NORMAL_SIZE + offsetSize, SELECT_COLOR, true);
else
data.setState(animatedValue, NORMAL_SIZE + offsetSize, Color.parseColor("#" + str_alpha + DEFAULT_COLOR), false);
}
});
valueAnimator.setDuration(100);
valueAnimator.start();
}
} else {
list.get(i).setState(0, NORMAL_SIZE, Color.parseColor("#"+DEFAULT_COLOR), false);
}
}
invalidate();
}
private void onActionMove(int indext, float downY) {
for (int i = 0; i < list.size(); i++) {
LetterData data = list.get(i);
if (i >= indext - 4 && i <= indext + 4) {
float distance = -ratio * (downY - getCenterY(i)) * (downY - getCenterY(i)) + MAX_DISTANCE;
if (distance < 0) distance = 0;
float scaleSize = distance / MAX_DISTANCE;
float offsetSize = (MAX_SIZE - NORMAL_SIZE) * scaleSize;
int alpha = (int) (255 * (1 - (distance / MAX_DISTANCE)));
String str_alpha = Integer.toHexString(alpha);
int length = str_alpha.length();
if (length < 2) {
str_alpha = "0" + str_alpha;
}
if (i == indext) {
data.setState(distance, NORMAL_SIZE + offsetSize, SELECT_COLOR, true);
} else {
data.setState(distance, NORMAL_SIZE + offsetSize, Color.parseColor("#" + str_alpha + DEFAULT_COLOR), false);
}
} else {
data.setState(0, NORMAL_SIZE, Color.parseColor("#"+DEFAULT_COLOR), false);
}
}
invalidate();
}
private OnSeletListener onSeletListener;
public void setOnSeletListener(OnSeletListener o) {
this.onSeletListener = o;
}
public interface OnSeletListener {
void onSelect(int position, String letter);
}
private float getCenterY(int index) {
int i = text_height / 2 + index * text_height;
return i;
}
class LetterData {
private float distance;
private float textSize;
private String text;
private int textColor;
private boolean isBlod;
public LetterData(String text, float distance, float textSize, int textColor, boolean isBlod) {
this.distance = distance;
this.textSize = textSize;
this.text = text;
this.textColor = textColor;
this.isBlod = isBlod;
}
public void setState(float distance, float textSize, int textColor, boolean isBlod) {
this.distance = distance;
this.textSize = textSize;
this.textColor = textColor;
this.isBlod = isBlod;
}
public boolean isBlod() {
return isBlod;
}
public void setBlod(boolean blod) {
isBlod = blod;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
}
public float getDistance() {
return distance;
}
public void setDistance(float distance) {
this.distance = distance;
}
public float getTextSize() {
return textSize;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
... prompt'''
Activity
package com.example.sunsh.letter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ArrayList<String> strings;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView listView = (ListView) findViewById(R.id.listview);
LetterIndext letterIndext = (LetterIndext) findViewById(R.id.letter);
final ArrayList<String> list = new ArrayList<>();
for (char i = 63; i <= 90; i++) {
list.add(i+"");
}
letterIndext.setList(list);
letterIndext.setOnSeletListener(new LetterIndext.OnSeletListener() {
@Override
public void onSelect(int position,String letter) {
int i = strings.indexOf(letter);
listView.setSelection(i);
}
});
findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,"keyi ",Toast.LENGTH_SHORT).show();
}
});
strings = new ArrayList<>();
for (char i = 63; i <= 90; i++) {
strings.add(i+"");
for (int j = 0; j < 10; j++) {
strings.add(i+""+i);
}
}
listView.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return strings.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
TextView textView = new TextView(MainActivity.this);
textView.setText(strings.get(i));
textView.setPadding(20,20,20,20);
return textView;
}
});
}
}
... prompt'''
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:id="@+id/activity_main"
android:layout_width="match_parent"
android:background="#70000000"
android:layout_height="match_parent"
tools:context="com.example.sunsh.letter.MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:text="Hello World!" />
<com.example.sunsh.letter.LetterIndext
android:id="@+id/letter"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
... prompt'''