基于Android的补充工具栏,具有精美的动画效果,实现字母快速检索列表信息类似城市列表效果。
一.效果图:
二.添加依赖方式快速实现:
1.添加依赖:
dependencies {
implementation 'com.github.AlexLiuSheng:AnimSideBar:1.0.0'
}
2.布局:
<com.allenliu.sidebar.SideBar
android:layout_alignParentRight="true"
android:textColor="@color/colorAccent"
android:textSize="12sp"
android:paddingRight="10dp"
android:layout_width="wrap_content"
android:id="@+id/bar"
android:layout_height="match_parent" />
3.监听:
bar.setOnStrSelectCallBack(new ISideBarSelectCallBack() {
@Override
public void onSelectStr(int index, String selectStr) {
Toast.makeText(SideBarDemoActivity.this,selectStr,Toast.LENGTH_SHORT).show();
}
});
三.自定义快速实现:
1.主函数代码:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.allenliu.sidebar.ISideBarSelectCallBack;
import com.allenliu.sidebar.SideBar;
public class SideBarDemoActivity extends AppCompatActivity {
private SideBar bar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bar= (SideBar) findViewById(R.id.bar);
bar.setOnStrSelectCallBack(new ISideBarSelectCallBack() {
@Override
public void onSelectStr(int index, String selectStr) {
// Toast.makeText(SideBarDemoActivity.this,selectStr,Toast.LENGTH_SHORT).show();
}
});
}
}
2.布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sidebar="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".SideBarDemoActivity">
<com.ceshi.sidebar.SideBar
android:id="@+id/bar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:paddingRight="10dp"
android:textColor="@color/colorAccent"
android:textSize="12sp" />
</RelativeLayout>
3.自定义SideBar.java
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* 自定义SideBar
*/
public class SideBar extends android.support.v7.widget.AppCompatTextView {
private 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", "#"};
private Paint textPaint;
private Paint bigTextPaint;
private ISideBarSelectCallBack callBack;
private float eventY;
private float w;
private float sideTextWidth;
/**
* 是否重新测量宽高
*/
private boolean isTouching = false;
private float itemH;
/**
* 振幅
*/
private float A = dp(100);
/**
* 波峰与bigText之间的距离
*/
private int gapBetweenText = dp(50);
/**
* 开口数量
*/
private int openCount = 13;
/**
* 字体缩放,基于textSize
*/
private float fontScale = 1;
private float bigTextSize;
public SideBar(Context context) {
super(context);
init(null);
}
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
/**
* set MaxFontScale
*
* @param fontScale
* @return
*/
public SideBar setFontScale(float fontScale) {
this.fontScale = fontScale;
return this;
}
public void setDataResource(String[] data) {
letters = data;
invalidate();
}
public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {
this.callBack = callBack;
}
public SideBar setBigTextSize(float bigTextSize) {
this.bigTextSize = bigTextSize;
bigTextPaint.setTextSize(bigTextSize);
// invalidate();
return this;
}
public SideBar setA(float a) {
A = a;
// invalidate();
return this;
}
public SideBar setGapBetweenText(int gapBetweenText) {
this.gapBetweenText = gapBetweenText;
// invalidate();
return this;
}
public SideBar setOpenCount(int openCount) {
this.openCount = openCount;
// invalidate();
return this;
}
private void caculateAW(int height) {
itemH = height * 1.0f / letters.length;
/**
* 开口宽度
*/
float opendWidth = itemH * openCount;
//角速度 2PI/t 周期
w = (float) (Math.PI * 2.0f / (opendWidth * 2));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
caculateAW(MeasureSpec.getSize(heightMeasureSpec));
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
viewWidth = !isTouching ? (int) (sideTextWidth + getPaddingLeft() + getPaddingRight()) : (int) (A + gapBetweenText + getBigTextWidth() + getPaddingLeft() + getPaddingRight());
}
// CLog.e("width:" + viewWidth + "height:" + MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(viewWidth, MeasureSpec.getSize(heightMeasureSpec));
}
private void init(AttributeSet attrs) {
// setPadding(dp(10), 0, dp(10), 0);
if (attrs != null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);
A = typedArray.getInteger(R.styleable.SideBar_A, dp(100));
fontScale = typedArray.getFloat(R.styleable.SideBar_fontScale, 1);
bigTextSize = typedArray.getFloat(R.styleable.SideBar_bigTextSize, getTextSize() * 3);
gapBetweenText = typedArray.getInteger(R.styleable.SideBar_gapBetweenText, dp(50));
openCount = typedArray.getInteger(R.styleable.SideBar_openCount, 13);
} else {
bigTextSize = getTextSize() * 3;
}
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(getCurrentTextColor());
textPaint.setTextSize(getTextSize());
textPaint.setTextAlign(Paint.Align.CENTER);
bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bigTextPaint.setColor(getCurrentTextColor());
bigTextPaint.setTextSize(bigTextSize);
bigTextPaint.setTextAlign(Paint.Align.CENTER);
float sideTextHeight = textPaint.getFontMetrics().descent - textPaint.getFontMetrics().ascent;
sideTextWidth = textPaint.measureText("W");
}
private int dp(int v) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (v * scale + 0.5f);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int startTouchX = (int) (getMeasuredWidth() - A);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (event.getX() > startTouchX) {
eventY = event.getY();
if (!isTouching) {
isTouching = true;
requestLayout();
} else {
invalidate();
}
} else {
if (isTouching) {
resetDefault();
}
}
return true;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
resetDefault();
return true;
}
return super.onTouchEvent(event);
}
private void resetDefault() {
isTouching = false;
eventY = 0;
requestLayout();
}
@Override
protected void onDraw(Canvas canvas) {
int singleSideCount = openCount / 2;
int index = isTouching && eventY >= 0 && eventY <= getMeasuredHeight() ? (int) Math.floor((eventY / itemH)) : -(singleSideCount + 1);
// index=Math.min(letters.length,index);
// CLog.e("index:" + index + "eventY:" + eventY);
float sideX = sideTextWidth / 2 + getPaddingRight();
for (int i = 0; i < letters.length; i++) {
//rest textsize
textPaint.setTextSize(getTextSize());
int y = (int) (itemH * (i + 1));
int x;
if (Math.abs(i - index) > singleSideCount) {
x = (int) (getMeasuredWidth() - sideX);
} else {
float percent = eventY / itemH;
int t = (int) (i * itemH - eventY);
double v = A * Math.sin(w * t + Math.PI / 2);
// //如果算出来小于字体宽度 就取字体宽度
v = Math.max(v, sideX);
x = (int) (getMeasuredWidth() - v);
//根据delta缩放字体
if (v == sideX) {
textPaint.setTextSize(getTextSize());
} else {
float delta = (Math.abs((i - percent)) / singleSideCount);
float textSize = getTextSize() + (1 - delta) * getTextSize() * fontScale;
// textSize=Math.max(textSize,getTextSize());
textPaint.setTextSize(textSize);
}
}
canvas.drawText(letters[i], x, y, textPaint);
}
if (index != -(singleSideCount + 1)) {
canvas.drawText(letters[index], getPaddingLeft() + getBigTextWidth() / 2, (int) (itemH * (index + 1)), bigTextPaint);
if (callBack != null) {
callBack.onSelectStr(index, letters[index]);
}
}
}
private float getBigTextWidth() {
return bigTextPaint.measureText("W");
}
}
4.定义接口:
/**
* 定义接口
*/
public interface ISideBarSelectCallBack {
void onSelectStr(int index,String selectStr);
}
5.attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SideBar">
<attr name="fontScale" format="float" />
<attr name="bigTextSize" format="float" />
<attr name="openCount" format="integer" />
<attr name="A" format="integer" />
<attr name="gapBetweenText" format="integer" />
</declare-styleable>
</resources>
其它案例: