概述

      本文只要说的是自定义一个刻度尺,正好学习下android自定义控件,之前写过一篇《Android自定义一个属于自己的时间钟表》,大家如果感兴趣可以去看下,好了不扯淡了,直接上效果:
看到这个效果以后估计好多新手会觉得不知道如何入手,但是要是大神看到了就会想用什么方式实现才是最好的。这就是差距啊,没办法像我这个菜鸟只好参考下其他实现方法,写了这个demo,让我们一起来看看实现思路。
我们来分步骤一步一步来实现:
1、绘制刻度尺及刻度值(高,中,低刻度)。
2、绘制底部线。
3、绘制中间箭头。
4、监听手势处理(处理范围越界,及选中)。
第一步:1、自定义View的属性,首先在res/values/  下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。
 <declare-styleable name="RulerView"><!--最大刻度的颜色--><attr name="mMaxScaleColor" format="color"/><!--中间刻度的颜色--><attr name="mMidScaleColor" format="color"/><!--最小刻度的颜色--><attr name="mMinScaleColor" format="color"/><!--底线的颜色--><attr name="mBottomLineColor" format="color"/><!--最大刻度的宽度--><attr name="mMaxScaleWidth" format="dimension"/><!--中间刻度的宽度--><attr name="mMidScaleWidth" format="dimension"/><!--最小刻度的宽度--><attr name="mMinScaleWidth" format="dimension"/><!--底线的宽度--><attr name="mBottomLineWidth" format="dimension"/><!--最大刻度的高度占控件的高度比例--><attr name="mMaxScaleHeightRatio" format="float"/><!--中间刻度的高度占控件的高度比例--><attr name="mMidScaleHeightRatio" format="float"/><!--最小刻度的高度占控件的高度比例--><attr name="mMinScaleHeightRatio" format="float"/><!--是否显示刻度值--><attr name="isShowScaleValue" format="boolean"/><!--是否刻度渐变 包括刻度值和刻度线及下面的线--><attr name="isScaleGradient" format="boolean"/><!--刻度值颜色--><attr name="mScaleValueColor" format="color"/><!--刻度值文字大小--><attr name="mScaleValueSize" format="dimension"/><!--刻度值间隔--><attr name="mScaleSpace" format="dimension"/><!-- 当前值--><attr name="mCurrentValue" format="integer"/><!--最大值--><attr name="mMaxValue" format="integer"/><!--最小值--><attr name="mMinValue" format="integer"/><!--刻度基数--><attr name="mScaleBase" format="integer"/><!--中间图标--><attr name="mMiddleImg" format="reference"/></declare-styleable>

通过这个attrs大家也看到,我们设置了还是蛮细的,所有的刻度及刻度值颜色大小都设定了,让控件设置更加的灵活。

2、下面创建一个class类为rulerview.class ,并设置main.xml中:
 <com.dalong.rulerview.RulerViewandroid:id="@+id/ruler2"android:layout_centerInParent="true"android:layout_width="match_parent"android:layout_height="wrap_content"app:mMaxValue="5000"app:mMinValue="1000"app:mScaleBase="100"app:mScaleSpace="10dp"app:mMaxScaleColor="@color/colorAccent"app:mMidScaleColor="@color/colorPrimary"app:mMinScaleColor="@color/colorPrimary"app:mBottomLineColor="@color/colorAccent"app:mMaxScaleHeightRatio="0.5"app:mMidScaleHeightRatio="0.3"app:mMinScaleHeightRatio="0.2"app:mMaxScaleWidth="2.5dp"app:mMidScaleWidth="2dp"app:mMinScaleWidth="2dp"app:mBottomLineWidth="2.5dp"app:mCurrentValue="1000"app:mScaleValueColor="@color/colorAccent"app:mScaleValueSize="12sp"app:mMiddleImg="@mipmap/icon_arrow"app:isScaleGradient="false"/>

3、在自定义View的构造方法中,获得我们的自定义的样式

 // 默认刻度模式public static final int MOD_TYPE_SCALE = 5;//刻度基数  每个刻度代表多少 默认为1public int mScaleBase=1;//最大刻度的颜色public int mMaxScaleColor;//中间刻度的颜色public int mMidScaleColor;//最小刻度的颜色public int mMinScaleColor;//底部线的颜色public int mBottomLineColor;//最大刻度的宽度public float mMaxScaleWidth;//中间刻度的宽度public float mMidScaleWidth;//最小刻度的宽度public float mMinScaleWidth;//底线的宽度public float mBottomLineWidth;//最大刻度的高度占控件的高度比例public float mMaxScaleHeightRatio;//中间刻度的高度占控件的高度比例public float mMidScaleHeightRatio;//最小刻度的高度占控件的高度比例public float mMinScaleHeightRatio;//是否显示刻度值public boolean isShowScaleValue;//是否刻度渐变public boolean isScaleGradient;//刻度值颜色public int  mScaleValueColor;//刻度值文字大小public float  mScaleValueSize;//当前值public int mCurrentValue;//最大值public int mMaxValue;//最小值public int mMinValue;//中间图片private Bitmap mMiddleImg;//刻度线画笔private  Paint mScalePaint;// 刻度值画笔private  TextPaint mScaleValuePaint;//中间图片画笔private  Paint mMiddleImgPaint;private  float mTpDesiredWidth;//最大刻度高度private int mMaxScaleHeight;//中间刻度高度private int mMidScaleHeight;//最小刻度高度private int mMinScaleHeight;// 滚动偏移量private int scrollingOffset;//间隔Sprivate int mScaleSpace=20;// 滚动器private RulerViewScroller scroller;// 是否执行滚动private boolean isScrollingPerformed;public RulerView(Context context) {this(context,null);}public RulerView(Context context, AttributeSet attrs) {this(context, attrs,0);}public RulerView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.RulerView);mMaxScaleColor=typedArray.getColor(R.styleable.RulerView_mMaxScaleColor, Color.BLACK);mMidScaleColor=typedArray.getColor(R.styleable.RulerView_mMidScaleColor, Color.BLACK);mMinScaleColor=typedArray.getColor(R.styleable.RulerView_mMinScaleColor, Color.BLACK);mMaxScaleColor=typedArray.getColor(R.styleable.RulerView_mMaxScaleColor, Color.BLACK);mScaleValueColor=typedArray.getColor(R.styleable.RulerView_mScaleValueColor, Color.BLACK);mBottomLineColor=typedArray.getColor(R.styleable.RulerView_mBottomLineColor, Color.BLACK);mMaxScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMaxScaleWidth, 15);mMidScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMidScaleWidth, 12);mMinScaleWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mMinScaleWidth, 10);mBottomLineWidth = typedArray.getDimensionPixelSize(R.styleable.RulerView_mBottomLineWidth, 15);mScaleValueSize = typedArray.getDimensionPixelSize(R.styleable.RulerView_mScaleValueSize, 12);mScaleSpace = typedArray.getDimensionPixelSize(R.styleable.RulerView_mScaleSpace, 20);mMaxScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMaxScaleHeightRatio, 0.3f);mMidScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMidScaleHeightRatio, 0.2f);mMinScaleHeightRatio = typedArray.getFloat(R.styleable.RulerView_mMinScaleHeightRatio, 0.1f);isShowScaleValue = typedArray.getBoolean(R.styleable.RulerView_isShowScaleValue, true);isScaleGradient = typedArray.getBoolean(R.styleable.RulerView_isScaleGradient, true);mMaxValue = typedArray.getInteger(R.styleable.RulerView_mMaxValue, 100);mMinValue = typedArray.getInteger(R.styleable.RulerView_mMinValue, 0);mScaleBase = typedArray.getInteger(R.styleable.RulerView_mScaleBase, 1);mCurrentValue = typedArray.getInteger(R.styleable.RulerView_mCurrentValue, 0);setCurrentValue(mCurrentValue);mMiddleImg = BitmapFactory.decodeResource(getResources(),typedArray.getResourceId(R.styleable.RulerView_mMiddleImg,R.drawable.ruler_mid_arraw));typedArray.recycle();mScalePaint=new Paint(Paint.ANTI_ALIAS_FLAG);mScalePaint.setStyle(Paint.Style.STROKE);mScalePaint.setAntiAlias(true);mScaleValuePaint=new TextPaint(Paint.ANTI_ALIAS_FLAG);mScaleValuePaint.setColor(mScaleValueColor);mScaleValuePaint.setTextSize(mScaleValueSize);mScaleValuePaint.setTextAlign(Paint.Align.CENTER);mTpDesiredWidth = Layout.getDesiredWidth("0", mScaleValuePaint);mMiddleImgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);mMiddleImgPaint.setStyle(Paint.Style.STROKE);mMiddleImgPaint.setAntiAlias(true);scroller=new RulerViewScroller(context,scrollingListener);}

4、我们重写onMesure:

 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(measureWidthSize(widthMeasureSpec),measureHeightSize(heightMeasureSpec));}private int measureHeightSize(int heightMeasureSpec) {int result;int mode=MeasureSpec.getMode(heightMeasureSpec);int size=MeasureSpec.getSize(heightMeasureSpec);if(mode==MeasureSpec.EXACTLY){result=size;}else{result=(int) (mMiddleImg.getHeight() + getPaddingTop() + getPaddingBottom() + 2 * mScaleValuePaint.getTextSize());if(mode==MeasureSpec.AT_MOST){result=Math.min(result,size);}}return  result;}private int measureWidthSize(int widthMeasureSpec) {int result;int mode=MeasureSpec.getMode(widthMeasureSpec);int size=MeasureSpec.getSize(widthMeasureSpec);if(mode==MeasureSpec.EXACTLY){result=size;}else{result=400;if(mode==MeasureSpec.AT_MOST){result=Math.min(result,size);}}return  result;}

5、设置下三种刻度尺的高度

  @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);if (w == 0 || h == 0)return;/*** 在这里根据控件高度设置三中刻度线的高度*/int mHeight = h - getPaddingTop() - getPaddingBottom();mMaxScaleHeight = (int) (mHeight*mMaxScaleHeightRatio);mMidScaleHeight = (int) (mHeight*mMidScaleHeightRatio);mMinScaleHeight = (int) (mHeight*mMinScaleHeightRatio);}

6、绘制刻度尺

 /*** 绘制刻度线* @param canvas* @param mDrawWidth* @param mDrawHeight*/private void drawScaleLine(Canvas canvas, int mDrawWidth, int mDrawHeight) {int scaleNum= (int) (Math.ceil(mDrawWidth/2f/mScaleSpace))+2;int distanceX = scrollingOffset;int currValue = mCurrentValue;drawScaleLine(canvas,scaleNum,distanceX,currValue,mDrawWidth,mDrawHeight);}/*** 绘制刻度线* @param canvas* @param scaleNum* @param distanceX* @param currValue* @param mDrawWidth* @param mDrawHeight*/private void drawScaleLine(Canvas canvas, int scaleNum, int distanceX, int currValue, int mDrawWidth, int mDrawHeight) {int dy = (int) (mDrawHeight - mTpDesiredWidth - mScaleValuePaint.getTextSize()) - getPaddingBottom();int value;float xPosition;for (int i=0;i<scaleNum;i++){// 右面xPosition=mDrawWidth/2f+i*mScaleSpace+distanceX;value=currValue+i;if(xPosition<=mDrawWidth && value>=(mMinValue/mScaleBase)&&value<=(mMaxValue/mScaleBase)){drawScaleLine(canvas, value,  xPosition, dy, scaleNum, i, mDrawHeight);}//绘制右面线if(value<(mMaxValue/mScaleBase)&&value>=(mMinValue/mScaleBase))drawBottomLine(canvas,getAlpha(scaleNum, i),xPosition-mMaxScaleWidth/2, dy, xPosition+mScaleSpace+mMaxScaleWidth/2, dy);//左面xPosition=mDrawWidth/2f-i*mScaleSpace+distanceX;value=currValue-i;if(xPosition>getPaddingLeft() && value>=(mMinValue/mScaleBase)&&value<=(mMaxValue/mScaleBase)){drawScaleLine( canvas, value,  xPosition, dy, scaleNum, i, mDrawHeight);}//绘制左面线if(value>=(mMinValue/mScaleBase) && value<(mMaxValue/mScaleBase))drawBottomLine(canvas,getAlpha(scaleNum, i),xPosition-mMaxScaleWidth/2, dy, xPosition+mScaleSpace+mMaxScaleWidth/2, dy);}}/*** 绘制底部线* @param canvas* @param alpha* @param sx* @param sy* @param ex* @param ey*/private void drawBottomLine(Canvas canvas,int alpha,float sx,float sy,float ex,float ey){mScalePaint.setColor(mBottomLineColor);mScalePaint.setStrokeWidth(mBottomLineWidth);mScalePaint.setAlpha(alpha);canvas.drawLine(sx, sy, ex, ey, mScalePaint);}/*** 绘制刻度尺  左  右* @param canvas* @param value* @param xPosition* @param dy* @param scaleNum* @param i* @param mDrawHeight*/public void drawScaleLine(Canvas canvas,int value, float xPosition,int dy,int scaleNum,int i,int mDrawHeight){if (value % MOD_TYPE_SCALE == 0) {if(value % (MOD_TYPE_SCALE*2)==0){//大刻度drawScaleLine(canvas,mMaxScaleWidth,mMaxScaleColor,getAlpha(scaleNum, i),xPosition,dy,xPosition,dy - mMaxScaleHeight);if (isShowScaleValue) {mScaleValuePaint.setAlpha(getAlpha(scaleNum, i));canvas.drawText(String.valueOf(value*mScaleBase), xPosition, mDrawHeight - mTpDesiredWidth, mScaleValuePaint);}}else{//中刻度drawScaleLine(canvas,mMidScaleWidth,mMidScaleColor,getAlpha(scaleNum, i),xPosition,dy,xPosition,dy-mMidScaleHeight);}}else{// 小刻度drawScaleLine(canvas,mMinScaleWidth,mMinScaleColor,getAlpha(scaleNum, i),xPosition,dy,xPosition,dy-mMinScaleHeight);}}/*** 绘制刻度尺刻度* @param canvas* @param strokeWidth* @param scaleColor* @param alpha* @param sx* @param sy* @param ex* @param ey*/private void drawScaleLine(Canvas canvas,float strokeWidth,int scaleColor,int alpha,float sx,float sy,float ex,float ey){mScalePaint.setStrokeWidth(strokeWidth);mScalePaint.setColor(scaleColor);mScalePaint.setAlpha(alpha);canvas.drawLine(sx, sy, ex, ey, mScalePaint);}

其实上面就是绘制刻度尺的核心办法,主要实现思想就是以中心为开始点向左右绘制刻度线。其中也有一些细节需要大家慢慢去思索的,比如这里包含高,中 ,低的三种刻度线,他们在绘制的时候高度,颜色,宽度都是不一样的设置。怎么区分,我这里也写的比较的清楚。这里我就不在提了。

7、绘制中间箭头图片
    /*** 绘制中间图片* @param canvas* @param mDrawWidth* @param mDrawHeight*/private void drawMiddleImg(Canvas canvas, int mDrawWidth, int mDrawHeight) {int left = (mDrawWidth - mMiddleImg.getWidth()) / 2;int top = (int) (mScaleValuePaint.getTextSize() / 2);canvas.drawBitmap(mMiddleImg, left, top, mMiddleImgPaint);}

以上写完就已经可以实现刻度尺了,但是刻度尺是无法拖动的,效果如下:

下面主要就是需要如何实现拖动的效果,其实这个才是最难的。
这里单独创建一个滑动控制类:
public class RulerViewScroller {//滚动的时间public static final int SCROLLING_DURATION = 400;//用于滚动的最小增量public static final int MIN_DELTA_FOR_SCROLLING = 1;//Listenerprivate ScrollingListener listener;//上下文private Context context;// Scrollingprivate GestureDetector gestureDetector;private Scroller scroller;private int lastScrollX;private float lastTouchedX;private boolean isScrollingPerformed;private final int MESSAGE_SCROLL = 0;private final int MESSAGE_JUSTIFY = 1;public RulerViewScroller(Context context, ScrollingListener listener) {this.listener = listener;this.context = context;gestureDetector = new GestureDetector(context, gestureListener);gestureDetector.setIsLongpressEnabled(false);scroller = new Scroller(context);scroller.setFriction(0.05f);}/*** 手势监听*/private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {return true;}public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {lastScrollX = 0;scroller.fling(0, lastScrollX, (int) -velocityX, 0, -0x7FFFFFFF, 0x7FFFFFFF, 0, 0);setNextMessage(MESSAGE_SCROLL);return true;}};/*** 手势处理* @param event* @return*/public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:lastTouchedX = event.getX();scroller.forceFinished(true);clearMessages();break;case MotionEvent.ACTION_MOVE:int distanceX = (int) (event.getX() - lastTouchedX);if (distanceX != 0) {startScrolling();listener.onScroll(distanceX);lastTouchedX = event.getX();}break;}//当手指离开控件时if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {justify();}return true;}/*** 发送下一步消息,清楚之前的消息* @param message*/private void setNextMessage(int message) {clearMessages();animationHandler.sendEmptyMessage(message);}/*** 清楚所有的what的消息列表*/private void clearMessages() {animationHandler.removeMessages(MESSAGE_SCROLL);animationHandler.removeMessages(MESSAGE_JUSTIFY);}/*** 滚动* @param distance 距离* @param time     时间*/public void scroll(int distance, int time) {scroller.forceFinished(true);lastScrollX = 0;scroller.startScroll(0, 0, distance, 0, time != 0 ? time : SCROLLING_DURATION);setNextMessage(MESSAGE_SCROLL);startScrolling();}/*** 动画处理handler*/private Handler animationHandler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {scroller.computeScrollOffset();int currX = scroller.getCurrX();int delta = lastScrollX - currX;lastScrollX = currX;if (delta != 0) {listener.onScroll(delta);}// 滚动是不是完成时,涉及到最终Y,所以手动完成if (Math.abs(currX - scroller.getFinalX()) < MIN_DELTA_FOR_SCROLLING) {lastScrollX = scroller.getFinalX();scroller.forceFinished(true);}if (!scroller.isFinished()) {animationHandler.sendEmptyMessage(msg.what);} else if (msg.what == MESSAGE_SCROLL) {justify();} else {finishScrolling();}return true;}});/*** 滚动停止时待校验*/private void justify() {listener.onJustify();setNextMessage(MESSAGE_JUSTIFY);}/*** 开始滚动*/private void startScrolling() {if (!isScrollingPerformed) {isScrollingPerformed = true;listener.onStarted();}}/*** 滚动结束*/void finishScrolling() {if (isScrollingPerformed) {listener.onFinished();isScrollingPerformed = false;}}/***  滚动监听器接口*/public interface ScrollingListener {/*** 正在滚动中回调* @param distance 滚动的距离*/void onScroll(int distance);/*** 启动滚动时调用的回调函数*/void onStarted();/*** 校验完成后 执行完毕后回调*/void onFinished();/*** 滚动停止时待校验*/void onJustify();}
然后在activity中:
 /*** 滚动回调接口*/RulerViewScroller.ScrollingListener scrollingListener = new RulerViewScroller.ScrollingListener() {/*** 滚动开始*/@Overridepublic void onStarted() {isScrollingPerformed = true;//滚动开始if (null != onWheelListener) {onWheelListener.onScrollingStarted(RulerView.this);}}/*** 滚动中* @param distance 滚动的距离*/@Overridepublic void onScroll(int distance) {doScroll(distance);}/*** 滚动结束*/@Overridepublic void onFinished() {if (outOfRange()) {return;}if (isScrollingPerformed) {//滚动结束if (null != onWheelListener) {onWheelListener.onScrollingFinished(RulerView.this);}isScrollingPerformed = false;}scrollingOffset = 0;invalidate();}/*** 验证滚动是否在正确位置*/@Overridepublic void onJustify() {if (outOfRange()) {return;}if (Math.abs(scrollingOffset) > RulerViewScroller.MIN_DELTA_FOR_SCROLLING) {if (scrollingOffset < -mScaleSpace / 2) {scroller.scroll(mScaleSpace + scrollingOffset, 0);} else if (scrollingOffset > mScaleSpace / 2) {scroller.scroll(scrollingOffset - mScaleSpace, 0);} else {scroller.scroll(scrollingOffset, 0);}}}};/*** 超出左右范围* @return*/private boolean outOfRange() {//这个是越界后需要回滚的大小值int outRange = 0;if (mCurrentValue < mMinValue/mScaleBase) {outRange = (mCurrentValue - mMinValue/mScaleBase) * mScaleSpace;} else if (mCurrentValue > mMaxValue/mScaleBase) {outRange = (mCurrentValue - mMaxValue/mScaleBase) * mScaleSpace;}if (0 != outRange) {scrollingOffset = 0;scroller.scroll(-outRange, 100);return true;}return false;}/*** 滚动中回调最新值* @param delta*/private void doScroll(int delta) {scrollingOffset += delta;int offsetCount = scrollingOffset / mScaleSpace;if (0 != offsetCount) {// 显示在范围内int oldValueIndex = Math.min(Math.max(mMinValue, mCurrentValue*mScaleBase), mMaxValue);mCurrentValue -= offsetCount;scrollingOffset -= offsetCount * mScaleSpace;if (null != onWheelListener) {//回调通知最新的值int valueIndex = Math.min(Math.max(mMinValue, mCurrentValue*mScaleBase), mMaxValue);onWheelListener.onChanged(this, oldValueIndex + "",valueIndex+"");}}invalidate();}private float mDownFocusX;private float mDownFocusY;private boolean isDisallowIntercept;@Overridepublic boolean onTouchEvent(MotionEvent event) {if (!isEnabled()) {return true;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mDownFocusX = event.getX();mDownFocusY = event.getY();break;case MotionEvent.ACTION_MOVE:if (!isDisallowIntercept && Math.abs(event.getY() - mDownFocusY) < Math.abs(event.getX() - mDownFocusX)) {isDisallowIntercept = true;if (getParent() != null) {getParent().requestDisallowInterceptTouchEvent(true);}}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:if (getParent() != null) {getParent().requestDisallowInterceptTouchEvent(false);}isDisallowIntercept = false;break;}return scroller.onTouchEvent(event);}private OnRulerViewScrollListener onWheelListener;/*** 添加滚动回调* @param listener the listener*/public void setScrollingListener(OnRulerViewScrollListener listener) {onWheelListener = listener;}public interface OnRulerViewScrollListener<T> {/*** 当更改选择的时候回调方法* @param rulerView 状态更改的view* @param oldValue  当前item的旧值* @param newValue  当前item的新值*/void onChanged(RulerView rulerView, T oldValue, T newValue);/*** 滚动启动时调用的回调方法* @param rulerView*/void onScrollingStarted(RulerView rulerView);/*** 滚动结束时调用的回调方法* @param rulerView*/void onScrollingFinished(RulerView rulerView);}

其中有些参考了网上实现方式并进行拓展以后就实现了下面的效果。

这里附上github:https://github.com/dalong982242260/AndroidRulerView

Android自定义一个属于自己的刻度尺相关推荐

  1. Android自定义一个车牌字母选择键盘

    在一般和车相关的应用,难免会和车牌打交道,组成车牌的要素,国内无非就是省份简称+地区代码+英文或者数字组成,比如京A12345,在需要输入车牌的功能上,就需要有省份简称键盘和英文数字键盘了,在上篇的文 ...

  2. Android自定义一个属于自己的时间钟表

    1.概述 本文主要讲解的是如何自定义一个时间钟表,通过简单的练习可以简单学习android当中自定义view的一些常用绘图技巧,优化android绘图操作.言归正传,首先看下我们需要实现的效果: 当我 ...

  3. Android自定义一个播放器控件

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actv ...

  4. 自定义一个可滑动时间刻度尺

    2019独角兽企业重金招聘Python工程师标准>>> 首先来张截图: 控件的外观可能不是很美观,不过功能基本都有了,可以自己设置选中的时间片段,暂时没有支持自定义样式... 接下来 ...

  5. Android自定义一个可伸展的ViewGroup

    /   今日科技快讯   / 近日多家媒体报道,有认证为阿里巴巴集团的员工在职场社交平台称,"88VIP积分将可以免费兑换腾讯视频会员,已经在内部灰度测试,预计双十一前上线".但是 ...

  6. Android自定义一个View实现运动的小人

    实现一个能动的人是我对模仿<奇怪的大冒险>第一阶段的最后一步,这一段时间学会了许多的东西,见到了很多大坑,也顺利脱险了.而本文所说的|能动的人是基于ImageView打造的.在ImageV ...

  7. Android自定义一个省份简称键盘

    hello啊各位老铁,这篇文章我们重新回到Android当中的自定义View,其实最近一直在搞Flutter,初步想法是,把Flutter当中的基础组件先封装一遍,然后接着各个工具类,列表,网络,统统 ...

  8. Android自定义一个对话框,完全自定义Android对话框AlertDialog的实现

    Android本身封装的AlertDialog.Builder很方便易用,但如果想要自定义弹出对话框的风格,如标题字体背景元素间隔之类的,那就比较困难了. 最近我就遇到了这个问题,一个工程的界面风格全 ...

  9. Android 自定义一个Toast

    前言 项目中 一般会封装一个Toast工具类 或者用第三方 自己写比较容易符合业务需求 也不难 来一起看看实现吧 public class ToastHelper {private static To ...

  10. Android自定义View实现方位刻度尺(类似于吃鸡手游)

    Android自定义View实现方位刻度尺(类似于吃鸡手游) 先上效果图 gif可能看不清,我下面放几张图片 原理解析 首先,我们应该把看得到的内容从上至下分成三部分:最上面的文字.中间的竖线和最下面 ...

最新文章

  1. PrestaShop 网站漏洞修复如何修复
  2. Github代码版本控制可视化教程—Git Gui的使用
  3. Docker(五):Docker Volume
  4. 耗时6个月整理的最全Java资源,限时删
  5. UA MATH575B 数值分析下III 图像恢复
  6. 【渝粤题库】陕西师范大学164111 Java及JSP动态网页编程与应用 作业 (高起专)
  7. 「SDOI2014」数数 解题报告
  8. Python快速学习03:运算 缩进和选择
  9. 工作中遇到的问题--使用注解进行增加删除修改的验证
  10. JVM&NIO&HashMap简单问
  11. 2014年上半年工作随记
  12. Oracle Instance
  13. SqlServer转换为Mysql(mss2sql)
  14. ES6 — 箭头函数
  15. c++ Primer plus 之c++学习
  16. 全球及中国电动车行业品牌竞争策略与投资机会分析报告2022版
  17. 千图成像python_Python---如何实现千图成像:初级篇(从图片爬取到图片合成)
  18. iptable 简析
  19. 【宝藏系列】如何解决word选中文字按backspace无法删除的问题
  20. 经典Android开发教程!面试字节跳动两轮后被完虐,附面试题答案

热门文章

  1. java基础——多态
  2. FileChannel阅读笔记
  3. CNCF的中国云原生调查报告
  4. 国家或地区内期货市场竞争格局的变迁
  5. java protected 构造方法_Java中protected语义解释
  6. Excel中经纬度格式化处理
  7. 桌面图标拖不动怎么办?
  8. 1月16日云栖精选夜读 | 阿里P8架构师谈:Zookeeper的原理和架构设计,以及应用场景...
  9. Python 计算思维训练——数组和曲线绘制练习(一)
  10. [C#] DataView用法