案例效果图:
要求:
实现如图所示效果,标题栏通过组合View的方式进行实现,统一对外暴露左侧按钮和右侧按钮点击
的方法,在点击右侧的回调方法中跳转到图二所示页面,图二的页面标题栏显示上一个页面组合View
的视图,通过自定义ViewGroup的方式实现梯形布局(下一个视图的起始位置是上一个视图的结束位置)。
图一中间的进度条使用继承自View的方式进行实现,并使用自定义属性的方式来设置进度条中间的颜色。
点击扫描二维码按钮之后,进度条开始以每秒10%的进度进行,当进度条走到100%后,跳转到图三的扫
描二维码页面,实现扫描二维码的功能。
通过组合View的方式实现标题栏
header.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/ib_left" android:layout_width="48dp" android:layout_height="48dp" android:layout_centerVertical="true" android:src="@mipmap/left" /> <ImageView android:id="@+id/ib_right" android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@mipmap/shenfen" /> <TextView android:id="@+id/tv_header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout>Header.java
package com.bawei.com.myapplication; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; /** * Created by Wangrx on 2017/11/4. */ public class Header extends RelativeLayout { private TextView mTextView; private ImageView ib_right; private ImageView ib_left; private String titleText; private int titleTextColor; private float titleTextSize; public Header(Context context) { super(context); } public Header(Context context, AttributeSet attrs) { super(context, attrs); //加载视图的布局 LayoutInflater.from(context).inflate(R.layout.header,this,true); //加载自定义的属性 TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.Header); titleText=a.getString(R.styleable.Header_titleText); titleTextColor=a.getColor(R.styleable.Header_titleTextColor, Color.WHITE); titleTextSize=a.getDimension(R.styleable.Header_titleTextSize,20f); //回收资源,这一句必须调用 a.recycle(); } /** * 此方法会在所有的控件都从xml文件中加载完成后调用 */ @Override protected void onFinishInflate() { super.onFinishInflate(); //获取子控件 mTextView= (TextView) findViewById(R.id.tv_header); ib_right= (ImageView) findViewById(R.id.ib_right); ib_left = findViewById(R.id.ib_left); //将从资源文件中加载的属性设置给子控件 if (!TextUtils.isEmpty(titleText)) setPageTitleText(titleText); setPageTitleTextColor(titleTextColor); setPageTitleTextSize(titleTextSize); } /** * 设置标题文字 * @param text */ public void setPageTitleText(String text) { mTextView.setText(text); } /** * 设置标题文字颜色 * @param color */ public void setPageTitleTextColor(int color) { mTextView.setTextColor(color); } /** * 设置标题文字大小 * @param size */ public void setPageTitleTextSize(float size) { mTextView.setTextSize(size); } /** * 设置按钮点击事件监听器 * @param listener */ public void setOnRightClickListener(OnClickListener listener) { ib_right.setOnClickListener(listener); } public void setOnLeftClickListener(OnClickListener listener) { ib_left.setOnClickListener(listener); } }通过自定义属性实现进度条
attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CirclePercentView"> <attr name="radius" format="dimension" /> <attr name="stripeWidth" format="dimension" /><!--色带宽度--> <attr name="percent" format="integer" /><!--百分比 最大值为100--> <attr name="smallColor" format="color" /><!--色带宽度--> <!--外圈颜色--> <attr name="bigColor" format="color" /> <!--中间字体颜色--> <attr name="centerTextSize" format="dimension" /> <!--色带宽度--> </declare-styleable> <declare-styleable name="Header"> <attr name="titleTextSize" format="dimension" /> <attr name="titleTextColor" format="color" /> <attr name="titleText" format="string"/> </declare-styleable> </resources>
PxUtils.java
package com.bawei.com.myapplication; import android.content.Context; import android.util.TypedValue; /** * Created by Wangrx on 2017/11/4. */ public class PxUtils { public static int dpToPx(int dp, Context context) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); } public static int spToPx(int sp, Context context) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics()); } }CirclePercentView.java
package com.bawei.com.myapplication; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * Created by Wangrx on 2017/11/4. */ public class CirclePercentView extends View{ //圆的半径 private float mRadius; //色带的宽度 private float mStripeWidth; //总体大小 private int mHeight; private int mWidth; //动画位置百分比进度 private int mCurPercent; //实际百分比进度 private int mPercent; //圆心坐标 private float x; private float y; //要画的弧度 private int mEndAngle; //小圆的颜色 private int mSmallColor; //大圆颜色 private int mBigColor; //中心百分比文字大小 private float mCenterTextSize; public CirclePercentView(Context context) { this(context, null); } public CirclePercentView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CirclePercentView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePercentView, defStyleAttr, 0); mStripeWidth = a.getDimension(R.styleable.CirclePercentView_stripeWidth, PxUtils.dpToPx(30, context)); mCurPercent = a.getInteger(R.styleable.CirclePercentView_percent, 0); mSmallColor = a.getColor(R.styleable.CirclePercentView_smallColor,0xffafb4db); mBigColor = a.getColor(R.styleable.CirclePercentView_bigColor,0xff6950a1); mCenterTextSize = a.getDimensionPixelSize(R.styleable.CirclePercentView_centerTextSize,PxUtils.spToPx(20,context)); mRadius = a.getDimensionPixelSize(R.styleable.CirclePercentView_radius,PxUtils.dpToPx(100,context)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取测量模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取测量大小 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) { mRadius = widthSize / 2; x = widthSize / 2; y = heightSize / 2; mWidth = widthSize; mHeight = heightSize; } if(widthMode == MeasureSpec.AT_MOST&&heightMode ==MeasureSpec.AT_MOST){ mWidth = (int) (mRadius*2); mHeight = (int) (mRadius*2); x = mRadius; y = mRadius; } setMeasuredDimension(mWidth,mHeight); } @Override protected void onDraw(Canvas canvas) { mEndAngle = (int) (mCurPercent * 3.6); //绘制大圆 Paint bigCirclePaint = new Paint(); bigCirclePaint.setAntiAlias(true); bigCirclePaint.setColor(mBigColor); canvas.drawCircle(x, y, mRadius, bigCirclePaint); //饼状图 Paint sectorPaint = new Paint(); sectorPaint.setColor(mSmallColor); sectorPaint.setAntiAlias(true); RectF rect = new RectF(0, 0, mWidth, mHeight); canvas.drawArc(rect, 270, mEndAngle, true, sectorPaint); //绘制小圆,颜色透明 Paint smallCirclePaint = new Paint(); smallCirclePaint.setAntiAlias(true); smallCirclePaint.setColor(mBigColor); canvas.drawCircle(x, y, mRadius - mStripeWidth, smallCirclePaint); //绘制文本 Paint textPaint = new Paint(); String text = mCurPercent + "%"; textPaint.setTextSize(mCenterTextSize); float textLength = textPaint.measureText(text); textPaint.setColor(Color.WHITE); canvas.drawText(text, x - textLength/2, y, textPaint); } //外部设置百分比数 public void setPercent(int percent) { if (percent > 100) { throw new IllegalArgumentException("percent must less than 100!"); } setCurPercent(percent); } //内部设置百分比 用于动画效果 private void setCurPercent(int percent) { mCurPercent = percent; CirclePercentView.this.postInvalidate(); } }自定义ViewGroup的方式实现梯形布局
MyViewGroup.java
package com.bawei.com.myapplication; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * Created by Wangrx on 2017/11/2. */ public class MyViewGroup extends ViewGroup{ public MyViewGroup(Context context) { super(context); } public MyViewGroup(Context context, AttributeSet attrs) { super(context, attrs); } public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec,heightMeasureSpec); } @Override protected void onLayout(boolean b, int i, int i1, int i2, int i3) { int count = getChildCount(); int startWidth = 0; int startHeight = 0; for (int l= 0;l<count;l++){ View v = getChildAt(l); v.layout(startWidth,startHeight,startWidth+v.getMeasuredWidth(),startHeight+v.getMeasuredHeight()); startWidth+=v.getMeasuredWidth(); startHeight+=v.getMeasuredHeight(); } } }实现二维码扫描
自定义zxing二维码扫描页面
CustomViewfinderView.java
package com.bawei.com.myapplication; import android.content.Context; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Rect; import android.graphics.Shader; import android.util.AttributeSet; import com.google.zxing.ResultPoint; import com.journeyapps.barcodescanner.ViewfinderView; import java.util.ArrayList; import java.util.List; /** * Created by Wangrx on 2017/11/4. */ public class CustomViewfinderView extends ViewfinderView { public int laserLinePosition=0; public float[] position=new float[]{0f,0.5f,1f}; public int[] colors=new int[]{0x00ffffff,0xffffffff,0x00ffffff}; public LinearGradient linearGradient ; public CustomViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); } /** * 重写draw方法绘制自己的扫描框 * @param canvas */ @Override public void onDraw(Canvas canvas) { refreshSizes(); if (framingRect == null || previewFramingRect == null) { return; } Rect frame = framingRect; Rect previewFrame = previewFramingRect; int width = canvas.getWidth(); int height = canvas.getHeight(); //绘制4个角 paint.setColor(0xFFFFFFFF);//定义画笔的颜色 canvas.drawRect(frame.left, frame.top, frame.left+70, frame.top+10, paint); canvas.drawRect(frame.left, frame.top, frame.left + 10, frame.top + 70, paint); canvas.drawRect(frame.right-70, frame.top, frame.right, frame.top+10, paint); canvas.drawRect(frame.right-10, frame.top, frame.right, frame.top+70, paint); canvas.drawRect(frame.left, frame.bottom-10, frame.left+70, frame.bottom, paint); canvas.drawRect(frame.left, frame.bottom-70, frame.left+10, frame.bottom, paint); canvas.drawRect(frame.right-70, frame.bottom-10, frame.right, frame.bottom, paint); canvas.drawRect(frame.right-10, frame.bottom-70, frame.right, frame.bottom, paint); // Draw the exterior (i.e. outside the framing rect) darkened paint.setColor(resultBitmap != null ? resultColor : maskColor); canvas.drawRect(0, 0, width, frame.top, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); canvas.drawRect(0, frame.bottom + 1, width, height, paint); if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle paint.setAlpha(CURRENT_POINT_OPACITY); canvas.drawBitmap(resultBitmap, null, frame, paint); } else { // paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); // scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; int middle = frame.height() / 2 + frame.top; laserLinePosition=laserLinePosition+5; if(laserLinePosition>frame.height()) { laserLinePosition=0; } linearGradient= new LinearGradient(frame.left + 1, frame.top+laserLinePosition , frame.right -1 , frame.top +10+laserLinePosition, colors, position, Shader.TileMode.CLAMP); // Draw a red "laser scanner" line through the middle to show decoding is active // paint.setColor(laserColor); paint.setShader(linearGradient); //绘制扫描线 canvas.drawRect(frame.left + 1, frame.top+laserLinePosition , frame.right -1 , frame.top +10+laserLinePosition, paint); paint.setShader(null); float scaleX = frame.width() / (float) previewFrame.width(); float scaleY = frame.height() / (float) previewFrame.height(); List<ResultPoint> currentPossible = possibleResultPoints; List<ResultPoint> currentLast = lastPossibleResultPoints; int frameLeft = frame.left; int frameTop = frame.top; if (currentPossible.isEmpty()) { lastPossibleResultPoints = null; } else { possibleResultPoints = new ArrayList<>(5); lastPossibleResultPoints = currentPossible; paint.setAlpha(CURRENT_POINT_OPACITY); paint.setColor(resultPointColor); for (ResultPoint point : currentPossible) { canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), POINT_SIZE, paint); } } if (currentLast != null) { paint.setAlpha(CURRENT_POINT_OPACITY / 2); paint.setColor(resultPointColor); float radius = POINT_SIZE / 2.0f; for (ResultPoint point : currentLast) { canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX), frameTop + (int) (point.getY() * scaleY), radius, paint); } } postInvalidateDelayed(16, frame.left , frame.top , frame.right , frame.bottom); // postInvalidate(); } } }CustomScanAct.java
package com.bawei.com.myapplication; import android.app.Activity; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.PersistableBundle; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.journeyapps.barcodescanner.CaptureManager; import com.journeyapps.barcodescanner.DecoratedBarcodeView; /** * Created by Wangrx on 2017/11/4. */ public class CustomScanAct extends Activity implements DecoratedBarcodeView.TorchListener { // 实现相关接口 // 添加一个按钮用来控制闪光灯,同时添加两个按钮表示其他功能,先用Toast表示 Button swichLight; DecoratedBarcodeView mDBV; private CaptureManager captureManager; private boolean isLightOn = false; @Override protected void onPause() { super.onPause(); captureManager.onPause(); } @Override protected void onResume() { super.onResume(); captureManager.onResume(); } @Override protected void onDestroy() { super.onDestroy(); captureManager.onDestroy(); } @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); captureManager.onSaveInstanceState(outState); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mDBV.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_customscan); swichLight = (Button) findViewById(R.id.btn_switch); mDBV= (DecoratedBarcodeView) findViewById(R.id.dbv_custom); mDBV.setTorchListener(this); // 如果没有闪光灯功能,就去掉相关按钮 if (!hasFlash()) { swichLight.setVisibility(View.GONE); } //重要代码,初始化捕获 captureManager = new CaptureManager(this, mDBV); captureManager.initializeFromIntent(getIntent(), savedInstanceState); captureManager.decode(); //选择闪关灯 swichLight.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isLightOn) { mDBV.setTorchOff(); } else { mDBV.setTorchOn(); } } }); } // torch 手电筒 @Override public void onTorchOn() { Toast.makeText(this, "torch on", Toast.LENGTH_LONG).show(); isLightOn = true; } @Override public void onTorchOff() { Toast.makeText(this, "torch off", Toast.LENGTH_LONG).show(); isLightOn = false; } // 判断是否有闪光灯功能 private boolean hasFlash() { return getApplicationContext().getPackageManager() .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); } }act_customscan.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_switch" android:layout_width="match_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:text="打开闪光灯" /> <com.journeyapps.barcodescanner.DecoratedBarcodeView android:id="@+id/dbv_custom" android:layout_above="@+id/btn_switch" android:layout_width="match_parent" android:layout_height="match_parent"></com.journeyapps.barcodescanner.DecoratedBarcodeView> </RelativeLayout>MainActivity.java
package com.bawei.com.myapplication; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; public class MainActivity extends AppCompatActivity { private int count = 0; private CirclePercentView circlePercentView; private Header header; private TextView tvResult; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (count<100){ count+=10; circlePercentView.setPercent(count); handler.sendEmptyMessageDelayed(0,1000); }else { Intent intent = new Intent(MainActivity.this, CustomScanAct.class); startActivity(intent); initEvent(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); header = (Header) findViewById(R.id.header); circlePercentView = (CirclePercentView) findViewById(R.id.circleView); Button button = (Button) findViewById(R.id.button); tvResult = (TextView) findViewById(R.id.tvResult); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { handler.sendEmptyMessageDelayed(0,1000); } }); header.setOnRightClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this,Main2Activity.class); startActivity(intent); } }); } private void initEvent() { //假如你要用的是fragment进行界面的跳转 //IntentIntegrator intentIntegrator = IntentIntegrator.forSupportFragment(ShopFragment.this).setCaptureActivity(CustomScanAct.class); IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this); intentIntegrator .setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES) .setPrompt("将二维码/条码放入框内,即可自动扫描")//写那句提示的话 .setOrientationLocked(false)//扫描方向固定 .setCaptureActivity(CustomScanAct.class) // 设置自定义的activity是CustomActivity .initiateScan(); // 初始化扫描 } //获取扫描的结果 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); if (intentResult != null) { if (intentResult.getContents() == null) { } else { // ScanResult 为获取到的字符串 String ScanResult = intentResult.getContents(); Log.d("zzz", "onActivityResult: "+ScanResult); tvResult.setText(ScanResult); } } else { super.onActivityResult(requestCode, resultCode, data); } } }activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bawei.com.myapplication.MainActivity" android:orientation="vertical" > <com.bawei.com.myapplication.Header android:id="@+id/header" android:layout_width="match_parent" android:layout_height="48dp" android:background="#b2bfc0" app:titleText="那些花儿" app:titleTextColor="#131313" app:titleTextSize="12sp"></com.bawei.com.myapplication.Header> <com.bawei.com.myapplication.CirclePercentView android:id="@+id/circleView" android:layout_below="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" app:stripeWidth="15dp" app:centerTextSize="16sp" app:percent="0" /> <Button android:id="@+id/button" android:layout_width="300dp" android:layout_height="wrap_content" android:layout_below="@+id/circleView" android:layout_centerHorizontal="true" android:text="扫描二维码"/> <TextView android:id="@+id/tvResult" android:layout_below="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout>Main2Activity.java
package com.bawei.com.myapplication; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; public class Main2Activity extends AppCompatActivity{ private Header header; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); header = (Header) findViewById(R.id.header); header.setOnLeftClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } }activity_main2.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.bawei.com.myapplication.Main2Activity"> <com.bawei.com.myapplication.Header android:id="@+id/header" android:layout_width="match_parent" android:layout_height="48dp" android:background="#b2bfc0" app:titleText="那些花儿" app:titleTextColor="#131313" app:titleTextSize="12sp"></com.bawei.com.myapplication.Header> <com.bawei.com.myapplication.MyViewGroup android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/header" > <TextView android:layout_width="120dp" android:layout_height="50dp" android:text="我是第一个" android:background="#2d9180" /> <TextView android:layout_width="120dp" android:layout_height="50dp" android:text="我是第二个" android:background="#9f94de" /> <TextView android:layout_width="120dp" android:layout_height="50dp" android:text="我是第三个" android:background="#8f6a26" /> </com.bawei.com.myapplication.MyViewGroup> </RelativeLayout>项目清单权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.FLASHLIGHT" />build.gradle添加依赖
compile 'com.journeyapps:zxing-android-embedded:3.3.0'