編輯:關於Android編程
由於某個項目需要,就寫了這個東西。大家看看有需求的直接拿走。
1.支持拖動,支持點擊
2.可定制的屬性還是比較全面的包裹差值器都可以設置,下面這個使用的是overshoot_interpolator差值器
我把這個卡尺選擇器封裝了一個自定義的popupwindow,大家可以直接使用這個popuwindow。
使用方法如下:
public class MainActivity extends AppCompatActivity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.tv_show); findViewById(R.id.btn_set).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showSet(); } }); } private void showSet() { //構造函數有三個參數,分別是context,要顯示的數據(如果直接在xml中設置了,就傳一個null就行了) //默認選中的index,選中的監聽器。 ScaleSelectorPopupWindow popupWindow = new ScaleSelectorPopupWindow(this, null, 0, new ScaleSelectorView.OnSelectChangeListener() { @Override public void onSelect(int index, String value) { textView.setText(value); } }); popupWindow.show(findViewById(android.R.id.content)); } }
實現沒有多難,不過有些東西還是可以說明一下,比如設置圓圈的陰影使用的是這個方法:
mCirclePaint = new Paint(); mCirclePaint.setStyle(Paint.Style.FILL); mCirclePaint.setColor(mCColor); mCirclePaint.setAntiAlias(true); //設置陰影 if (!isInEditMode()) { //關閉硬件加速,圓形的陰影才會出現 setLayerType(LAYER_TYPE_SOFTWARE, null); int color = mCirclePaint.getColor(); int newColor = Color.argb(50, Color.red(color), Color.green(color), Color.blue(color)); mCirclePaint.setShadowLayer(5, 0, 10, newColor); }
通過xml設置差值器,用到了AnimationUtils
int interpolatorID=array.getResourceId(R.styleable.ScaleSelectorView_ScaleSelectorInterpolator,0); if(interpolatorID!=0) { mInterpolator = AnimationUtils.loadInterpolator(context, interpolatorID); }
package com.zgh.scaleselector.view; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.zgh.scaleselector.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Created by zhuguohui on 2016/8/9. */ public class ScaleSelectorView extends View { Paint mLinePaint, mCirclePaint, mTextPaint; ListmData = new ArrayList<>(); int offset = 100, width, height; int lineHeight = 30; int cX = -1, cY, cR = 50; private List linesList; private int textOffSet = 140; //默認的index private int defaultIndex = 0; //文字和線段的顏色 private int mStyleColor; //圓的顏色 private int mCColor; //文字大小 private int mTextSize; private TimeInterpolator mInterpolator=null; public ScaleSelectorView(Context context) { this(context, null); } public ScaleSelectorView(Context context, AttributeSet attrs) { super(context, attrs); initAttr(context, attrs); initPaints(); } private void initAttr(Context context, AttributeSet attrs) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ScaleSelectorView); mCColor = array.getColor(R.styleable.ScaleSelectorView_ScaleSelectorCColor, Color.RED); mStyleColor = array.getColor(R.styleable.ScaleSelectorView_ScaleSelectorStyleColor, Color.parseColor("#AAAAAA")); mTextSize = array.getDimensionPixelSize(R.styleable.ScaleSelectorView_ScaleSelecorTextSize, 50); int dataID = array.getResourceId(R.styleable.ScaleSelectorView_ScaleSelectorData, 0); if (dataID != 0) { String[] arrayData = context.getResources().getStringArray(dataID); mData.clear(); Collections.addAll(mData, arrayData); } defaultIndex = array.getInt(R.styleable.ScaleSelectorView_ScaleSelectorDefaultIndex, 0); cR=array.getDimensionPixelOffset(R.styleable.ScaleSelectorView_ScaleSelectorCRadius,50); int interpolatorID=array.getResourceId(R.styleable.ScaleSelectorView_ScaleSelectorInterpolator,0); if(interpolatorID!=0) { mInterpolator = AnimationUtils.loadInterpolator(context, interpolatorID); } array.recycle(); } private void initPaints() { mLinePaint = new Paint(); mLinePaint.setColor(mStyleColor); mLinePaint.setStrokeWidth(5); mLinePaint.setStyle(Paint.Style.FILL); mCirclePaint = new Paint(); mCirclePaint.setStyle(Paint.Style.FILL); mCirclePaint.setColor(mCColor); mCirclePaint.setAntiAlias(true); //設置陰影 if (!isInEditMode()) { //關閉硬件加速,圓形的陰影才會出現 setLayerType(LAYER_TYPE_SOFTWARE, null); int color = mCirclePaint.getColor(); int newColor = Color.argb(50, Color.red(color), Color.green(color), Color.blue(color)); mCirclePaint.setShadowLayer(5, 0, 10, newColor); } mTextPaint = new Paint(); mTextPaint.setColor(mStyleColor); mTextPaint.setTextSize(mTextSize); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //計算文字位置 width = getWidth() - getPaddingLeft() - getPaddingRight() - cR; height = getHeight() - getPaddingTop() - getPaddingBottom(); //計算兩個刻度之間的間隔 offset = width / (mData.size() - 1); linesList = new ArrayList<>(); int l = getPaddingLeft() + cR / 2; for (int i = 0; i < mData.size(); i++) { Point point = new Point(l + i * offset, getHeight() / 2 + 5); linesList.add(point); } if (cX == -1) { if (linesList.size() > 0) { cX = linesList.get(defaultIndex).x; } else { cX = getWidth() / 2; } } cY = getHeight() / 2; } @Override protected void onDraw(Canvas canvas) { drawLines(canvas); drawTexts(canvas); drawCircle(canvas); } private void drawCircle(Canvas canvas) { canvas.drawCircle(cX, cY, cR, mCirclePaint); } private void drawTexts(Canvas canvas) { for (int i = 0; i < mData.size(); i++) { int x; Point point = linesList.get(i); Rect mBound = new Rect(); mTextPaint.getTextBounds(mData.get(i), 0, mData.get(i).length(), mBound); int width = mBound.width(); if (i == 0) { x = point.x; } else if (i == (mData.size() - 1)) { x = point.x - width; } else { x = point.x - width / 2; } canvas.drawText(mData.get(i), x, point.y - textOffSet, mTextPaint); } } public void setData(List data, int defaultIndex) { if (data == null) { throw new RuntimeException("Data 不能為空"); } if (defaultIndex < 0 || defaultIndex >= data.size()) { throw new RuntimeException("defaultIndex 無效"); } this.mData = data; this.defaultIndex = defaultIndex; requestLayout(); } private void drawLines(Canvas canvas) { //畫橫線 canvas.drawLine(getPaddingLeft() + cR / 2, getHeight() / 2 + 5, getWidth() - getPaddingRight() - cR / 2, getHeight() / 2 + 5, mLinePaint); for (int i = 0; i < linesList.size(); i++) { Point point = linesList.get(i); int height = lineHeight; if (i == 0 || i == linesList.size() - 1) { height += 20; } canvas.drawLine(point.x, point.y, point.x, point.y - height, mLinePaint); } } int dx, dy; boolean isPressOnCenter = false; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dx = (int) event.getX(); dy = (int) event.getY(); isPressOnCenter = calculateIsInCenter(dx, dy); break; case MotionEvent.ACTION_MOVE: if (isPressOnCenter) { int mx = (int) (event.getX() - dx); dx = (int) event.getX(); cX += mx; invalidate(); } break; case MotionEvent.ACTION_UP: if (isPressOnCenter) { animatToCloserPoint(cX); } else { //如果是點擊則計算最近的點 boolean isClick = Math.abs(event.getX() - dx) < 10 && Math.abs(event.getY() - dy) < 10; if (isClick) { animatToCloserPoint((int) event.getX()); } } break; } return true; } private void animatToCloserPoint(int x) { //計算最近的點 final int index = (int) ((x - getPaddingLeft() + 0.5 * offset) / offset); if (index >= linesList.size()) { return; } int moveX = linesList.get(index).x; ValueAnimator animator = ValueAnimator.ofInt(cX, moveX); if(mInterpolator!=null) { animator.setInterpolator(mInterpolator); } animator.setDuration(200); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int mx = (int) animation.getAnimatedValue(); cX = mx; invalidate(); } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mOnSelectChangeListener != null) { mOnSelectChangeListener.onSelect(index,mData.get(index)); } } }); animator.start(); } private boolean calculateIsInCenter(int dx, int dy) { return (dx - cX) * (dx - cX) + (dy - cY) * (dy - cY) <= (cR * cR) / 1.5; } public static interface OnSelectChangeListener { void onSelect(int index,String value); } private OnSelectChangeListener mOnSelectChangeListener; public void setOnSelectChangeListener(OnSelectChangeListener listener) { this.mOnSelectChangeListener = listener; } }
/** * Created by zhuguohui on 2016/8/9. */ public class ScaleSelectorPopupWindow extends PopupWindow { private Activity activity; private ScaleSelectorView scaleSelectorView; public ScaleSelectorPopupWindow(Context context, Listdata, int defaultIndex, ScaleSelectorView.OnSelectChangeListener listener) { if (!(context instanceof Activity)) { throw new RuntimeException("must use activity context"); } activity = (Activity) context; LayoutInflater inflater = LayoutInflater.from(context); View view = inflater.inflate(R.layout.layout_set, null); setContentView(view); setWidth(WindowManager.LayoutParams.MATCH_PARENT); setHeight(WindowManager.LayoutParams.WRAP_CONTENT); this.setFocusable(true); this.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); this.setOutsideTouchable(true); // 設置popWindow的顯示和消失動畫 this.setAnimationStyle(R.style.mypopwindow_anim_style); //屏幕變暗 changeWindowAlpha(0.6f); view.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); this.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { changeWindowAlpha(1.0f); } }); scaleSelectorView = (ScaleSelectorView) view.findViewById(R.id.selceView); if(data!=null) { scaleSelectorView.setData(data, defaultIndex); } scaleSelectorView.setOnSelectChangeListener(listener); } public void show(View view) { // 在底部顯示 this.showAtLocation(view, Gravity.BOTTOM, 0, 0); } private void changeWindowAlpha(float alpha) { Window window = activity.getWindow(); WindowManager.LayoutParams wl = window.getAttributes(); wl.alpha = alpha; //這句就是設置窗口裡崆件的透明度的.0.0全透明.1.0不透明. window.setAttributes(wl); } }
https://github.com/zhuguohui/ScaleSelector
上一篇文章Android 中的 Service 全面總結詳解【下】 介紹了Service的一些知識以及本地Service的使用,如果對Service還不太了解的建議先看下
Android Design Support Library使用詳解Google在2015的IO大會上,給我們帶來了更加詳細的Material Design設計規范,同時
Android屏幕適配出現的原因在我們學習如何進行屏幕適配之前,我們需要先了解下為什麼Android需要進行屏幕適配。由於Android系統的開放性,任何用戶、開發者、O
ViewRoot和DecorViewViewRoot對應於ViewRootImpl類,是連接WindowManager和DecorView的紐帶,View的三大流程均是通