Android仿支付宝订单确认和支付

本文介绍了一个支付确认对话框及自定义密码输入视图的实现过程,包括XML布局设计、自定义View的绘制逻辑及动画效果。通过具体的代码示例展示了如何在Android应用中实现这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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>


8. 源码


9.特别感谢




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值