Android仿斗鱼滑动登录验证
2016-04-29 09:53
771 查看
最近事情不是很多,在家无聊的时候看看直播,总能看到一些新奇的验证登录方式,正好自己最近也要去熟悉一下新的开发工具Android Studio,所以打算自己实现一下.
先看一下效果图:
1.)需要定义一个阴影部分去遮盖图片,确定一个目标位置.
2.)需要一个带有边框的滑块内容,初始位置在最左边.
3.)阴影部分和滑块可以随机旋转,并保持一致.
4.)需要一个拖拽条使滑块随着拖拽条条拖拽而移动
5.)判断是否验证成功
第二点,需要用到画笔的setXfermode方法来设置图片叠加的显示模式.从而显示出一个带有边框的滑块.
第三点,需要改变图片的matrix来实现对图像的旋转.
第四点,安卓系统自带的SeekBar可以实现拖拽条的功能.
第五点,只需对外提供一个回调接口,来判断是否验证成功即可.
然后我们创建一个DouYuView继承自ImageView即可,来定义一下属性
准备好这些之后,开始实现具体功能,这次我们打算用星星的图片,来设置阴影和滑块,首先我们准备两张图片,如下图(注意,带边框的图片中间部分,使用白色填充的):
为了使图片不会出现变形等异常,我们需要对图片进行获取及缩放的一些操作,代码如下:
然后再去生成阴影图像和滑块
准备好这些之后,还需要随机生成一个显示位置和对图像进行旋转的方法就可以在画布上去画出这些内容,代码如下:
我们还需要对控件进行重置、控制滑块移动、提供回调方法判断验证是否成功等方法.
基本上我们需要的功能就实现了.
MainActivity:
xml:
实现的效果图如下:
下载地址
先看一下效果图:
一、确认需求
首先我们确认一下需求,我们分步来看1.)需要定义一个阴影部分去遮盖图片,确定一个目标位置.
2.)需要一个带有边框的滑块内容,初始位置在最左边.
3.)阴影部分和滑块可以随机旋转,并保持一致.
4.)需要一个拖拽条使滑块随着拖拽条条拖拽而移动
5.)判断是否验证成功
二、分析问题
第一点,很简单我们只需要一直带有透明度的阴影图片,随机的去覆盖在图像上就可以实现.第二点,需要用到画笔的setXfermode方法来设置图片叠加的显示模式.从而显示出一个带有边框的滑块.
第三点,需要改变图片的matrix来实现对图像的旋转.
第四点,安卓系统自带的SeekBar可以实现拖拽条的功能.
第五点,只需对外提供一个回调接口,来判断是否验证成功即可.
三、代码实现
首先创建一个attr文件来定义一些自定义属性,方便我们使用<?xml version="1.0" encoding="utf-8"?> <resources> <!--滑块的高度--> <attr name="unitHeight" format="dimension" /> <!--滑块的宽度--> <attr name="unitWidth" format="dimension" /> <!--滑块占图片高度的比例--> <attr name="unitHeightScale" format="integer" /> <!--滑块占图片宽度的比例--> <attr name="unitWidthScale" format="integer" /> <!--滑块边框的图片资源--> <attr name="unitShadeSrc" format="reference" /> <!--阴影部分的图片资源--> <attr name="unitShowSrc" format="reference" /> <!--是否需要旋转--> <attr name="needRotate" format="boolean" /> <!--验证时的误差值--> <attr name="deviate" format="integer" /> <declare-styleable name="DouYuView"> <attr name="unitHeight" /> <attr name="unitWidth" /> <attr name="unitHeightScale" /> <attr name="unitWidthScale" /> <attr name="unitShadeSrc" /> <attr name="unitShowSrc" /> <attr name="needRotate" /> <attr name="deviate" /> </declare-styleable> </resources>
然后我们创建一个DouYuView继承自ImageView即可,来定义一下属性
/** * 定义画笔 */ private Paint mPaint; /** * 验证的图像 */ private Bitmap mBitmap; /** * 验证滑块的高 */ private int mUintHeight; /** * 验证滑块的宽 */ private int mUintWidth; /** * 验证滑块宽占用整体图片大小的比例,默认1/5 */ private int mUnitWidthScale; /** * 验证滑块高度占用整体图片大小的比例,默认1/4 */ private int mUnitHeightScale; /** * 随机生成滑块的X坐标 */ private int mUnitRandomX; /** * 随机生成滑块的Y坐标 */ private int mUnitRandomY; /*** * 滑块移动的距离 */ private float mUnitMoveDistance = 0; /*** * 滑块图像 */ private Bitmap mUnitBp; /** * 验证位置图像 */ private Bitmap mShowBp; /** * 背景阴影图像 */ private Bitmap mShadeBp; /** * 是否需要旋转 **/ private boolean needRotate; /** * 旋转的角度 */ private int rotate; /** * 判断是否完成的偏差量,默认为10 */ public int DEFAULT_DEVIATE; /** * 判断是否重新绘制图像 */ private boolean isReSet = true;
准备好这些之后,开始实现具体功能,这次我们打算用星星的图片,来设置阴影和滑块,首先我们准备两张图片,如下图(注意,带边框的图片中间部分,使用白色填充的):
为了使图片不会出现变形等异常,我们需要对图片进行获取及缩放的一些操作,代码如下:
/** * 获取实际显示的图片 * * @return */ public Bitmap getBaseBitmap() { Bitmap b = drawableToBitamp(getDrawable()); float scaleX = 1.0f; float scaleY = 1.0f; // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; scaleX = getWidth() * 1.0f / b.getWidth(); scaleY = getHeight() * 1.0f / b.getHeight(); Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); Bitmap bd = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true); return bd; } /** * drawable转bitmap * * @param drawable * @return */ private Bitmap drawableToBitamp(Drawable drawable) { if (null == drawable) { return null; } if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; } /** * 缩放图片 * * @param bp * @param x * @param y * @return */ public static Bitmap handleBitmap(Bitmap bp, float x, float y) { int w = bp.getWidth(); int h = bp.getHeight(); float sx = (float) x / w; float sy = (float) y / h; Matrix matrix = new Matrix(); matrix.postScale(sx, sy); Bitmap resizeBmp = Bitmap.createBitmap(bp, 0, 0, w, h, matrix, true); return resizeBmp; }
然后再去生成阴影图像和滑块
/** * 创建遮挡的图片(阴影部分) * * @return */ private Bitmap drawTargetBitmap() { // 绘制图片 Bitmap showB; if (null != mShowBp) { showB = handleBitmap(mShowBp, mUintWidth, mUintHeight); } else { showB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_show), mUintWidth, mUintHeight); } // 如果需要旋转图片,进行旋转,旋转后为了保持和滑块大小一致,需要重新缩放比例 if (needRotate) { showB = handleBitmap(rotateBitmap(rotate, showB), mUintWidth, mUintHeight); } return showB; } /** * 创建结合的图片(滑块) * * @param bp */ private Bitmap drawResultBitmap(Bitmap bp) { // 绘制图片 Bitmap shadeB; if (null != mShadeBp) { shadeB = handleBitmap(mShadeBp, mUintWidth, mUintHeight); } else { shadeB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_shade), mUintWidth, mUintHeight); } // 如果需要旋转图片,进行旋转,旋转后为了和画布大小保持一致,避免出现图像显示不全,需要重新缩放比例 if (needRotate) { shadeB = handleBitmap(rotateBitmap(rotate, shadeB), mUintWidth, mUintHeight); } Bitmap resultBmp = Bitmap.createBitmap(mUintWidth, mUintHeight, Bitmap.Config.ARGB_8888); Paint paint = new Paint(); paint.setAntiAlias(true); Canvas canvas = new Canvas(resultBmp); canvas.drawBitmap(shadeB, new Rect(0, 0, mUintWidth, mUintHeight), new Rect(0, 0, mUintWidth, mUintHeight), paint); // 选择交集去上层图片 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); canvas.drawBitmap(bp, new Rect(0, 0, mUintWidth, mUintHeight), new Rect(0, 0, mUintWidth, mUintHeight), paint); return resultBmp; }
准备好这些之后,还需要随机生成一个显示位置和对图像进行旋转的方法就可以在画布上去画出这些内容,代码如下:
/** * 随机生成生成滑块的XY坐标 */ private void initUnitXY() { mUnitRandomX = (int) (Math.random() * (mBitmap.getWidth() - mUintWidth)); mUnitRandomY = (int) (Math.random() * (mBitmap.getHeight() - mUintHeight)); // 防止生成的位置距离太近 if (mUnitRandomX <= mBitmap.getWidth() / 2) { mUnitRandomX = mUnitRandomX + mBitmap.getWidth() / 4; } // 防止生成的X坐标截图时导致异常 if (mUnitRandomX + mUintWidth > getWidth()) { initUnitXY(); return; } } /** * 旋转图片 * * @param degree * @param bitmap * @return */ public Bitmap rotateBitmap(int degree, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postRotate(degree); Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return bm; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isReSet) { mBitmap = getBaseBitmap(); if (0 == mUintWidth) { mUintWidth = mBitmap.getWidth() / mUnitWidthScale; } if (0 == mUintHeight) { mUintHeight = mBitmap.getHeight() / mUnitHeightScale; } initUnitXY(); mUnitBp = Bitmap.createBitmap(mBitmap, mUnitRandomX, mUnitRandomY, mUintWidth, mUintHeight); } isReSet = false; canvas.drawBitmap(drawTargetBitmap(), mUnitRandomX, mUnitRandomY, mPaint); canvas.drawBitmap(drawResultBitmap(mUnitBp), mUnitMoveDistance, mUnitRandomY, mPaint); }
我们还需要对控件进行重置、控制滑块移动、提供回调方法判断验证是否成功等方法.
/** * 重置 */ public void reSet() { isReSet = true; mUnitMoveDistance = 0; if (needRotate) { rotate = (int) (Math.random() * 3) * 90; } else { rotate = 0; } invalidate(); } /** * 获取每次滑动的平均偏移值 * * @return */ public float getAverageDistance(int max) { return (float) (mBitmap.getWidth() - mUintWidth) / max; } /** * 滑块移动距离 * * @param distance */ public void setUnitMoveDistance(float distance) { mUnitMoveDistance = distance; // 防止滑块滑出图片 if (mUnitMoveDistance > mBitmap.getWidth() - mUintWidth) { mUnitMoveDistance = mBitmap.getWidth() - mUintWidth; } invalidate(); } /** * 拼图成功的回调 **/ interface onPuzzleListener { public void onSuccess(); public void onFail(); } /** * 回调 */ private onPuzzleListener mlistener; /** * 设置回调 * * @param listener */ public void setPuzzleListener(onPuzzleListener listener) { this.mlistener = listener; } /** * 验证是否拼接成功 */ public void testPuzzle() { if (Math.abs(mUnitMoveDistance - mUnitRandomX) <= DEFAULT_DEVIATE) { if (null != mlistener) { mlistener.onSuccess(); } } else { if (null != mlistener) { mlistener.onFail(); } } }
基本上我们需要的功能就实现了.
四、完整的代码及使用
DouYuView:package com.example.junweiliu.douyutest; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.widget.ImageView; /** * Created by junweiliu on 16/4/26. */ public class DouYuView extends ImageView { /** * 定义画笔 */ private Paint mPaint; /** * 验证的图像 */ private Bitmap mBitmap; /** * 验证滑块的高 */ private int mUintHeight; /** * 验证滑块的宽 */ private int mUintWidth; /** * 验证滑块宽占用整体图片大小的比例,默认1/5 */ private int mUnitWidthScale; /** * 验证滑块高度占用整体图片大小的比例,默认1/4 */ private int mUnitHeightScale; /** * 随机生成滑块的X坐标 */ private int mUnitRandomX; /** * 随机生成滑块的Y坐标 */ private int mUnitRandomY; /*** * 滑块移动的距离 */ private float mUnitMoveDistance = 0; /*** * 滑块图像 */ private Bitmap mUnitBp; /** * 验证位置图像 */ private Bitmap mShowBp; /** * 背景阴影图像 */ private Bitmap mShadeBp; /** * 是否需要旋转 **/ private boolean needRotate; /** * 旋转的角度 */ private int rotate; /** * 判断是否完成的偏差量,默认为10 */ public int DEFAULT_DEVIATE; /** * 判断是否重新绘制图像 */ private boolean isReSet = true; /** * 拼图成功的回调 **/ interface onPuzzleListener { public void onSuccess(); public void onFail(); } /** * 回调 */ private onPuzzleListener mlistener; /** * 设置回调 * * @param listener */ public void setPuzzleListener(onPuzzleListener listener) { this.mlistener = listener; } public DouYuView(Context context) { this(context, null); } public DouYuView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DouYuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DouYuView); mUintWidth = ta.getDimensionPixelOffset(R.styleable.DouYuView_unitHeight, 0); mUintHeight = ta.getDimensionPixelOffset(R.styleable.DouYuView_unitHeight, 0); mUnitHeightScale = ta.getInteger(R.styleable.DouYuView_unitHeightScale, 4); mUnitWidthScale = ta.getInteger(R.styleable.DouYuView_unitWidthScale, 5); Drawable showBp = ta.getDrawable(R.styleable.DouYuView_unitShowSrc); mShowBp = drawableToBitamp(showBp); Drawable shadeBp = ta.getDrawable(R.styleable.DouYuView_unitShadeSrc); mShadeBp = drawableToBitamp(shadeBp); needRotate = ta.getBoolean(R.styleable.DouYuView_needRotate, true); DEFAULT_DEVIATE = ta.getInteger(R.styleable.DouYuView_deviate, 10); ta.recycle(); // 初始化 mPaint = new Paint(); mPaint.setAntiAlias(true); if (needRotate) { rotate = (int) (Math.random() * 3) * 90; } else { rotate = 0; } } /** * 随机生成生成滑块的XY坐标 */ private void initUnitXY() { mUnitRandomX = (int) (Math.random() * (mBitmap.getWidth() - mUintWidth)); mUnitRandomY = (int) (Math.random() * (mBitmap.getHeight() - mUintHeight)); // 防止生成的位置距离太近 if (mUnitRandomX <= mBitmap.getWidth() / 2) { mUnitRandomX = mUnitRandomX + mBitmap.getWidth() / 4; } // 防止生成的X坐标截图时导致异常 if (mUnitRandomX + mUintWidth > getWidth()) { initUnitXY(); return; } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isReSet) { mBitmap = getBaseBitmap(); if (0 == mUintWidth) { mUintWidth = mBitmap.getWidth() / mUnitWidthScale; } if (0 == mUintHeight) { mUintHeight = mBitmap.getHeight() / mUnitHeightScale; } initUnitXY(); mUnitBp = Bitmap.createBitmap(mBitmap, mUnitRandomX, mUnitRandomY, mUintWidth, mUintHeight); } isReSet = false; canvas.drawBitmap(drawTargetBitmap(), mUnitRandomX, mUnitRandomY, mPaint); canvas.drawBitmap(drawResultBitmap(mUnitBp), mUnitMoveDistance, mUnitRandomY, mPaint); } /** * 重置 */ public void reSet() { isReSet = true; mUnitMoveDistance = 0; if (needRotate) { rotate = (int) (Math.random() * 3) * 90; } else { rotate = 0; } invalidate(); } /** * 获取每次滑动的平均偏移值 * * @return */ public float getAverageDistance(int max) { return (float) (mBitmap.getWidth() - mUintWidth) / max; } /** * 滑块移动距离 * * @param distance */ public void setUnitMoveDistance(float distance) { mUnitMoveDistance = distance; // 防止滑块滑出图片 if (mUnitMoveDistance > mBitmap.getWidth() - mUintWidth) { mUnitMoveDistance = mBitmap.getWidth() - mUintWidth; } invalidate(); } /** * 验证是否拼接成功 */ public void testPuzzle() { if (Math.abs(mUnitMoveDistance - mUnitRandomX) <= DEFAULT_DEVIATE) { if (null != mlistener) { mlistener.onSuccess(); } } else { if (null != mlistener) { mlistener.onFail(); } } } /** * 创建遮挡的图片(阴影部分) * * @return */ private Bitmap drawTargetBitmap() { // 绘制图片 Bitmap showB; if (null != mShowBp) { showB = handleBitmap(mShowBp, mUintWidth, mUintHeight); } else { showB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_show), mUintWidth, mUintHeight); } // 如果需要旋转图片,进行旋转,旋转后为了保持和滑块大小一致,需要重新缩放比例 if (needRotate) { showB = handleBitmap(rotateBitmap(rotate, showB), mUintWidth, mUintHeight); } return showB; } /** * 创建结合的图片(滑块) * * @param bp */ private Bitmap drawResultBitmap(Bitmap bp) { // 绘制图片 Bitmap shadeB; if (null != mShadeBp) { shadeB = handleBitmap(mShadeBp, mUintWidth, mUintHeight); } else { shadeB = handleBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.puzzle_shade), mUintWidth, mUintHeight); } // 如果需要旋转图片,进行旋转,旋转后为了和画布大小保持一致,避免出现图像显示不全,需要重新缩放比例 if (needRotate) { shadeB = handleBitmap(rotateBitmap(rotate, shadeB), mUintWidth, mUintHeight); } Bitmap resultBmp = Bitmap.createBitmap(mUintWidth, mUintHeight, Bitmap.Config.ARGB_8888); Paint paint = new Paint(); paint.setAntiAlias(true); Canvas canvas = new Canvas(resultBmp); canvas.drawBitmap(shadeB, new Rect(0, 0, mUintWidth, mUintHeight), new Rect(0, 0, mUintWidth, mUintHeight), paint); // 选择交集去上层图片 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); canvas.drawBitmap(bp, new Rect(0, 0, mUintWidth, mUintHeight), new Rect(0, 0, mUintWidth, mUintHeight), paint); return resultBmp; } /** * 获取实际显示的图片 * * @return */ public Bitmap getBaseBitmap() { Bitmap b = drawableToBitamp(getDrawable()); float scaleX = 1.0f; float scaleY = 1.0f; // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; scaleX = getWidth() * 1.0f / b.getWidth(); scaleY = getHeight() * 1.0f / b.getHeight(); Matrix matrix = new Matrix(); matrix.setScale(scaleX, scaleY); Bitmap bd = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true); return bd; } /** * drawable转bitmap * * @param drawable * @return */ private Bitmap drawableToBitamp(Drawable drawable) { if (null == drawable) { return null; } if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; } /** * 缩放图片 * * @param bp * @param x * @param y * @return */ public static Bitmap handleBitmap(Bitmap bp, float x, float y) { int w = bp.getWidth(); int h = bp.getHeight(); float sx = (float) x / w; float sy = (float) y / h; Matrix matrix = new Matrix(); matrix.postScale(sx, sy); Bitmap resizeBmp = Bitmap.createBitmap(bp, 0, 0, w, h, matrix, true); return resizeBmp; } /** * 旋转图片 * * @param degree * @param bitmap * @return */ public Bitmap rotateBitmap(int degree, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postRotate(degree); Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return bm; } }
MainActivity:
package com.example.junweiliu.douyutest; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.SeekBar; import android.widget.Toast; public class MainActivity extends Activity { /** * 滑块 */ private SeekBar mSeekBar; /** * 自定义的控件 */ private DouYuView mDY; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mDY = (DouYuView) findViewById(R.id.dy_v); mSeekBar = (SeekBar) findViewById(R.id.sb_dy); mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) { // Log.e("main", "当前位置" + i); mDY.setUnitMoveDistance(mDY.getAverageDistance(seekBar.getMax()) * i); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { mDY.testPuzzle(); } }); mDY.setPuzzleListener(new DouYuView.onPuzzleListener() { @Override public void onSuccess() { // mSeekBar.setEnabled(false); Toast.makeText(MainActivity.this, "验证成功", Toast.LENGTH_SHORT).show(); mSeekBar.setProgress(0); mDY.reSet(); } @Override public void onFail() { Toast.makeText(MainActivity.this, "验证失败", Toast.LENGTH_SHORT).show(); mSeekBar.setProgress(0); } }); } }
xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:dy="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:gravity="center" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.junweiliu.douyutest.MainActivity"> <com.example.junweiliu.douyutest.DouYuView android:id="@+id/dy_v" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:src="@mipmap/test" dy:unitHeight="60dp" dy:unitWidth="80dp" dy:unitShowSrc="@mipmap/star_show" dy:unitShadeSrc="@mipmap/star_shade" dy:needRotate="true" /> <SeekBar android:id="@+id/sb_dy" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" /> </LinearLayout>
实现的效果图如下:
下载地址
相关文章推荐
- 安卓从业者应该关注:Android 6.0的运行时权限
- Android代码混淆及项目发布步骤记录
- Android EditText点击两次才能响应,解决办法
- android事件分发之ViewGroup篇
- Android--解决EditText放到popupWindow中,原有复制、粘贴、全选、选择功能失效问题
- 20145206实验四《Android开发基础》
- android事件分发机制之View篇
- Android通过Servcie实现Notification定时发送
- 一个Activity掌握Android5.0新控件
- 监听android home键后台运行
- Android LayoutInflater
- iOS-Android Android自定义日历,可以点击、标注日期、节气、旧历等
- 如何在Windows下搭建Android开发环境
- Android ——Toolbar开发实践总结(转)
- Android—— Fragment 真正的完全解析(上)(转)
- android 学习路线
- Android SQLite的使用
- Android中线程那些事
- android 在一个应用中启动另一个应用
- Android中线程那些事