1.效果图
2.实现方式
dialog_confirm_order.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">
<LinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:textColor="#000000"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="在线支付"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_close"
android:textColor="#000000"
android:text="X"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="30dp">
<TextView
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="支付金额"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="¥50.00"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="30dp">
<TextView
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="订单信息"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="某某某购买了十桶泡面"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="30dp">
<TextView
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="付款方式"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="账户余额"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="40dp" />
<Button
android:id="@+id/btn_confirmorder"
android:background="#9bbdf2"
android:text="立即支付"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
3.dialog_input_password.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">
<LinearLayout
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<TextView
android:id="@+id/tv_close"
android:text="X"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="请输入支付密码"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.mama.paydemo.widget.PayPsdInputView
android:inputType="number"
android:id="@+id/et_password"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_gravity="right"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingRight="40dp"
android:gravity="right"
android:text="忘记密码?"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
4.自定义输入框PayPsdInputView.java
package com.example.mama.paydemo.widget;
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.text.InputFilter;
import android.util.AttributeSet;
import android.widget.EditText;
import com.example.mama.paydemo.R;
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
/**
* Created by mama on 2017/12/5.
*/
public class PayPsdInputView extends EditText {
private Context mContext;
/**
* 第一个圆开始绘制的圆心坐标
*/
private float startX;
private float startY;
private float cX;
/**
* 实心圆的半径
*/
private int radius = 10;
/**
* view的高度
*/
private int height;
private int width;
/**
* 当前输入密码位数
*/
private int textLength = 0;
private int bottomLineLength;
/**
* 最大输入位数
*/
private int maxCount = 6;
/**
* 圆的颜色 默认BLACK
*/
private int circleColor = Color.BLACK;
/**
* 底部线的颜色 默认GRAY
*/
private int bottomLineColor = Color.GRAY;
/**
* 分割线的颜色
*/
private int borderColor = Color.GRAY;
/**
* 分割线的画笔
*/
private Paint borderPaint;
/**
* 分割线开始的坐标x
*/
private int divideLineWStartX;
/**
* 分割线的宽度 默认2
*/
private int divideLineWidth = 2;
/**
* 竖直分割线的颜色
*/
private int divideLineColor = Color.GRAY;
private int focusedColor = Color.RED;
private RectF rectF = new RectF();
private RectF focusedRecF = new RectF();
private int psdType = 0;
private final static int psdType_weChat = 0;
private final static int psdType_bottomLine = 1;
/**
* 矩形边框的圆角
*/
private int rectAngle = 0;
/**
* 竖直分割线的画笔
*/
private Paint divideLinePaint;
/**
* 圆的画笔
*/
private Paint circlePaint;
/**
* 底部线的画笔
*/
private Paint bottomLinePaint;
/**
* 当前输入的位置索引
*/
private int position = 0;
private onPasswordListener mListener;
public PayPsdInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
getAtt(attrs);
initPaint();
this.setBackgroundColor(Color.TRANSPARENT);
this.setCursorVisible(false);
this.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxCount)});
}
private void getAtt(AttributeSet attrs) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.PayPsdInputView);
maxCount = typedArray.getInt(R.styleable.PayPsdInputView_maxCount, maxCount);
circleColor = typedArray.getColor(R.styleable.PayPsdInputView_circleColor, circleColor);
bottomLineColor = typedArray.getColor(R.styleable.PayPsdInputView_bottomLineColor, bottomLineColor);
radius = typedArray.getDimensionPixelOffset(R.styleable.PayPsdInputView_radius, radius);
divideLineWidth = typedArray.getDimensionPixelSize(R.styleable.PayPsdInputView_divideLineWidth, divideLineWidth);
divideLineColor = typedArray.getColor(R.styleable.PayPsdInputView_divideLineColor, divideLineColor);
psdType = typedArray.getInt(R.styleable.PayPsdInputView_psdType, psdType);
rectAngle = typedArray.getDimensionPixelOffset(R.styleable.PayPsdInputView_rectAngle, rectAngle);
focusedColor = typedArray.getColor(R.styleable.PayPsdInputView_focusedColor, focusedColor);
typedArray.recycle();
}
/**
* 初始化画笔
*/
private void initPaint() {
circlePaint = getPaint(5, Paint.Style.FILL, circleColor);
bottomLinePaint = getPaint(2, Paint.Style.FILL, bottomLineColor);
borderPaint = getPaint(3, Paint.Style.STROKE, borderColor);
divideLinePaint = getPaint(divideLineWidth, Paint.Style.FILL, borderColor);
}
/**
* 设置画笔
*
* @param strokeWidth 画笔宽度
* @param style 画笔风格
* @param color 画笔颜色
* @return
*/
private Paint getPaint(int strokeWidth, Paint.Style style, int color) {
Paint paint = new Paint(ANTI_ALIAS_FLAG);
paint.setStrokeWidth(strokeWidth);
paint.setStyle(style);
paint.setColor(color);
paint.setAntiAlias(true);
return paint;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height = h;
width = w;
divideLineWStartX = w / maxCount;
startX = w / maxCount / 2;
startY = h / 2;
bottomLineLength = w / (maxCount + 2);
rectF.set(0, 0, width, height);
}
@Override
protected void onDraw(Canvas canvas) {
//不删除的画会默认绘制输入的文字
// super.onDraw(canvas);
switch (psdType) {
case psdType_weChat:
drawWeChatBorder(canvas);
drawItemFocused(canvas, position);
break;
case psdType_bottomLine:
drawBottomBorder(canvas);
break;
}
drawPsdCircle(canvas);
}
/**
* 画微信支付密码的样式
*
* @param canvas
*/
private void drawWeChatBorder(Canvas canvas) {
canvas.drawRoundRect(rectF, rectAngle, rectAngle, borderPaint);
for (int i = 0; i < maxCount - 1; i++) {
canvas.drawLine((i + 1) * divideLineWStartX,
0,
(i + 1) * divideLineWStartX,
height,
divideLinePaint);
}
}
private void drawItemFocused(Canvas canvas, int position) {
if (position > maxCount - 1) {
return;
}
focusedRecF.set(position * divideLineWStartX, 0, (position + 1) * divideLineWStartX,
height);
canvas.drawRoundRect(focusedRecF, rectAngle, rectAngle, getPaint(3, Paint.Style.STROKE, focusedColor));
}
/**
* 画底部显示的分割线
*
* @param canvas
*/
private void drawBottomBorder(Canvas canvas) {
for (int i = 0; i < maxCount; i++) {
cX = startX + i * 2 * startX;
canvas.drawLine(cX - bottomLineLength / 2,
height,
cX + bottomLineLength / 2,
height, bottomLinePaint);
}
}
/**
* 画密码实心圆
*
* @param canvas
*/
private void drawPsdCircle(Canvas canvas) {
for (int i = 0; i < textLength; i++) {
canvas.drawCircle(startX + i * 2 * startX,
startY,
radius,
circlePaint);
}
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
this.position = start + lengthAfter;
textLength = text.toString().length();
if (textLength>0&&textLength == maxCount) {
mListener.getInput(getPasswordString());
}
invalidate();
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
//保证光标始终在最后
if (selStart == selEnd) {
setSelection(getText().length());
}
}
/**
* 获取输入的密码
*
* @return
*/
public String getPasswordString() {
return getText().toString().trim();
}
public void getComparePassword(onPasswordListener listener) {
mListener = listener;
}
/**
* 密码监听
*/
public interface onPasswordListener {
void getInput(String data);
}
}
5.调用 MainActivity
package com.example.mama.paydemo;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.example.mama.paydemo.widget.PayPsdInputView;
/**
* Created by dingkangkang on 2017/12/5.
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void zhifu(View view){
final AlertDialog dialog = new AlertDialog.Builder(this).create();
Window w = dialog.getWindow();
w.setGravity(Gravity.BOTTOM);
//给弹窗加滑出动画
w.setWindowAnimations(R.style.DialogAnimation);
//设置外围点击消失
dialog.setCanceledOnTouchOutside(true);
//加上会去掉边界
dialog.getWindow().setBackgroundDrawable(new ColorDrawable());
dialog.show();
//以下必须写在show之后,不然会失效
WindowManager wm = w.getWindowManager();
Display d = wm.getDefaultDisplay();//屏幕
WindowManager.LayoutParams p = w.getAttributes();
p.width = (int)(d.getWidth()*1);//设置弹窗宽度为屏幕的宽度
w.setAttributes(p);
View v = LayoutInflater.from(this).inflate(R.layout.dialog_confirm_order,null);
dialog.setContentView(v);//给弹窗加载布局
TextView tv_close = (TextView)v.findViewById(R.id.tv_close);
Button btn_confirmorder = (Button)v.findViewById(R.id.btn_confirmorder);
btn_confirmorder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
showPayDialog();
}
});
tv_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
//输入密码弹窗
public void showPayDialog(){
final AlertDialog dialog = new AlertDialog.Builder(this).create();
Window w = dialog.getWindow();
w.setGravity(Gravity.BOTTOM);
//给弹窗加滑出动画
w.setWindowAnimations(R.style.DialogAnimation);
//设置外围点击消失
dialog.setCanceledOnTouchOutside(true);
//加上会去掉边界
dialog.getWindow().setBackgroundDrawable(new ColorDrawable());
dialog.show();
//以下必须写在show之后,不然会失效
WindowManager wm = w.getWindowManager();
Display d = wm.getDefaultDisplay();//屏幕
WindowManager.LayoutParams p = w.getAttributes();
p.width = (int)(d.getWidth()*1);//设置弹窗宽度为屏幕的宽度
w.setAttributes(p);
View v = LayoutInflater.from(this).inflate(R.layout.dialog_input_password,null);
dialog.setContentView(v);//给弹窗加载布局
TextView tv_close = (TextView)v.findViewById(R.id.tv_close);
PayPsdInputView payPsdInputView = (PayPsdInputView)v.findViewById(R.id.et_password);
//弹出软键盘, AlertDialog中一般的显示方法不起作用
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,0);
payPsdInputView.getComparePassword(new PayPsdInputView.onPasswordListener() {
@Override
public void getInput(String data) {
//此步骤要在 dialog.dismiss(); 之前执行,不然会失效
if (imm.isActive()){
hideSoft();
}
dialog.dismiss();
Toast.makeText(getApplication(),"密码:::::"+data, Toast.LENGTH_SHORT).show();
}
});
tv_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//此步骤要在 dialog.dismiss(); 之前执行,不然会失效
if (imm.isActive()){
hideSoft();
}
dialog.dismiss();
}
});
}
//隐藏软键盘
private void hideSoft() {
//隐藏键盘,AlertDialog中一般的隐藏方法不起作用
InputMethodManager imm2 = (InputMethodManager) getSystemService(MainActivity.INPUT_METHOD_SERVICE);
imm2.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
}
}
6.弹窗动画 请看
7.自定义动画属性 在values下新建 attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 支付属性 -->
<declare-styleable name="PayPsdInputView">
<attr name="maxCount" format="integer" />
<attr name="circleColor" format="color" />
<attr name="bottomLineColor" format="color" />
<attr name="radius" format="reference" />
<attr name="divideLineWidth" format="dimension" />
<attr name="divideLineColor" format="color" />
<attr name="rectAngle" format="dimension" />
<attr name="focusedColor" format="color"/>
<attr name="psdType" format="enum">
<enum name="weChat" value="0" />
<enum name="bottomLine" value="1" />
</attr>
</declare-styleable>
<declare-styleable name="CircleProgressBarView">
<attr name="circleBgStrokeWidth" format="dimension" />
<attr name="progressStrokeWidth" format="dimension" />
<attr name="circleBgColor" format="color" />
<attr name="progressColor" format="color" />
<attr name="circleAnimationDuration" format="integer" />
<attr name="isDrawCenterProgressText" format="boolean" />
<attr name="centerProgressTextColor" format="color"/>
<attr name="centerProgressTextSize" format="dimension"/>
</declare-styleable>
<declare-styleable name="RadarWaveView">
<attr name="waveColor" format="color" />
<attr name="waveAmplitude" format="dimension" />
<attr name="waveSpeed" format="float" />
<attr name="waveStartPeriod" format="float" />
<attr name="waveStart" format="boolean" />
<attr name="waveFillTop" format="boolean" />
<attr name="waveFillBottom" format="boolean" />
<attr name="waveFillType" format="enum">
<enum name="top" value="0" />
<enum name="bottom" value="1" />
</attr>
<attr name="waveType" format="enum">
<enum name="sin" value="0" />
<enum name="cos" value="1" />
</attr>
</declare-styleable>
</resources>
9.特别感谢