Android 自定义密码输入框和键盘(仿支付宝支付页面)

最近在项目中需要用到支付页面,就仿支付宝支付页面自定义了一个密码输入框
1.自定义密码框View

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.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;

/**
 * Email [email protected]
 * Created by Darren on 2016/12/24.
 * Version 1.0
 * Description: 自定义输入密码框
 */
public class PsdEditText extends EditText {
    // 画笔
    private Paint mPaint;
    // 一个密码所占的宽度
    private int mPasswordItemWidth;
    // 密码的个数默认为6位数
    private int mPasswordNumber = 6;
    // 背景边框颜色
    private int mBgColor = Color.parseColor("#d1d2d6");
    // 背景边框大小
    private int mBgSize = 1;
    // 背景边框圆角大小
    private int mBgCorner = 0;
    // 分割线的颜色
    private int mDivisionLineColor = mBgColor;
    // 分割线的大小
    private int mDivisionLineSize = 1;
    // 密码圆点的颜色
    private int mPasswordColor = mDivisionLineColor;
    // 密码圆点的半径大小
    private int mPasswordRadius = 4;

    public PsdEditText(Context context) {
        this(context, null);
    }

    public PsdEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttributeSet(context, attrs);
        initPaint();
        // 默认只能够设置数字和字母
        setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint = new Paint();
        // 抗锯齿
        mPaint.setAntiAlias(true);
        // 防抖动
        mPaint.setDither(true);
    }

    /**
     * 初始化属性
     */
    private void initAttributeSet(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PsdEditText);
        // 获取大小
        mDivisionLineSize = (int) array.getDimension(R.styleable.PsdEditText_divisionLineSize, dip2px(mDivisionLineSize));
        mPasswordRadius = (int) array.getDimension(R.styleable.PsdEditText_passwordRadius, dip2px(mPasswordRadius));
        mBgSize = (int) array.getDimension(R.styleable.PsdEditText_bgSize, dip2px(mBgSize));
        mBgCorner = (int) array.getDimension(R.styleable.PsdEditText_bgCorner, 0);
        // 获取颜色
        mBgColor = array.getColor(R.styleable.PsdEditText_bgColor, mBgColor);
        mDivisionLineColor = array.getColor(R.styleable.PsdEditText_divisionLineColor, mDivisionLineColor);
        mPasswordColor = array.getColor(R.styleable.PsdEditText_passwordColor, mDivisionLineColor);
        array.recycle();
    }

    /**
     * dip 转 px
     */
    private float dip2px(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dip, getResources().getDisplayMetrics());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 一个密码的宽度
        mPasswordItemWidth = (getWidth() - 2 * mBgSize - (mPasswordNumber - 1) * mDivisionLineSize) / mPasswordNumber;
        // 画背景
        drawBg(canvas);
        // 画分割线
        drawDivisionLine(canvas);
        // 画密码
        drawPassword(canvas);

        // 当前密码是不是满了
        if(mListener != null){
            String password = getText().toString().trim();
            if(password.length()>=mPasswordNumber){
                mListener.passwordFull(password);
            }
        }
    }

    /**
     * 绘制密码
     */
    private void drawPassword(Canvas canvas) {
        // 密码绘制是实心
        mPaint.setStyle(Paint.Style.FILL);
        // 设置密码的颜色
        mPaint.setColor(mPasswordColor);
        // 获取当前text
        String text = getText().toString().trim();
        // 获取密码的长度
        int passwordLength = text.length();
        // 不断的绘制密码
        for (int i = 0; i < passwordLength; i++) {
            int cy = getHeight() / 2;
            int cx = mBgSize + i * mPasswordItemWidth + i * mDivisionLineSize + mPasswordItemWidth / 2;
            canvas.drawCircle(cx, cy, mPasswordRadius, mPaint);
        }
    }

    /**
     * 绘制分割线
     */
    private void drawDivisionLine(Canvas canvas) {
        // 给画笔设置大小
        mPaint.setStrokeWidth(mDivisionLineSize);
        // 设置分割线的颜色
        mPaint.setColor(mDivisionLineColor);
        for (int i = 0; i < mPasswordNumber - 1; i++) {
            int startX = mBgSize + (i + 1) * mPasswordItemWidth + i * mDivisionLineSize;
            int startY = mBgSize;
            int endX = startX;
            int endY = getHeight() - mBgSize;
            canvas.drawLine(startX, startY, endX, endY, mPaint);
        }
    }

    /**
     * 绘制背景
     */
    private void drawBg(Canvas canvas) {
        RectF rect = new RectF(mBgSize, mBgSize, getWidth() - mBgSize, getHeight() - mBgSize);
        // 给画笔设置大小
        mPaint.setStrokeWidth(mBgSize);
        // 设置背景的颜色
        mPaint.setColor(mBgColor);
        // 画空心
        mPaint.setStyle(Paint.Style.STROKE);

        // 绘制背景  drawRect , drawRoundRect  ,
        // 如果有圆角那么就绘制drawRoundRect,否则绘制drawRect
        if (mBgCorner == 0) {
            canvas.drawRect(rect, mPaint);
        } else {
            canvas.drawRoundRect(rect, mBgCorner, mBgCorner, mPaint);
        }
    }

    /**
     * 添加一个密码
     */
    public void addPassword(String number) {
        // 把之前的密码取出来
        String password = getText().toString().trim();
        if (password.length() >= mPasswordNumber) {
            // 密码不能超过当前密码个输
            return;
        }
        // 密码叠加
        password += number;
        setText(password);
    }

    /**
     * 删除最后一位密码
     */
    public void deleteLastPassword() {
        String password = getText().toString().trim();
        // 判断当前密码是不是空
        if (TextUtils.isEmpty(password)) {
            return;
        }
        password = password.substring(0, password.length() - 1);
        setText(password);
    }

    // 设置当前密码是否已满的接口回掉
    private PasswordFullListener mListener;
    public void setOnPasswordFullListener(PasswordFullListener listener){
        this.mListener = listener;
    }
    /**
     * 密码已经全部填满
     */
    public interface PasswordFullListener {
        public void passwordFull(String password);
    }
}

2.自定义键盘

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class CustomKeyboard extends LinearLayout implements View.OnClickListener {
    public CustomKeyboard(Context context) {
        this(context, null);
    }

    public CustomKeyboard(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 直接加载布局
        inflate(context,R.layout.ui_custom_keyboard,this);
        setItemClickListener(this);
    }

    /**
     * 设置子View的ClickListener
     */
    private void setItemClickListener(View view) {
        if(view instanceof ViewGroup){
            ViewGroup viewGroup = (ViewGroup) view;
            int childCount = viewGroup.getChildCount();
            for (int i=0;i<childCount;i++){
                //不断的递归给里面所有的View设置OnClickListener
                View childView = viewGroup.getChildAt(i);
                setItemClickListener(childView);
            }
        }else{
            view.setOnClickListener(this);
        }
    }

    @Override
    public void onClick(View v) {
        if(v instanceof TextView){
            // 点击的是数字
            String number = ((TextView)v).getText().toString().trim();
            if(mListener != null){
                mListener.click(number);
            }
        }

        if(v instanceof ImageView){
            // 点击的是删除
            if(mListener != null){
                mListener.delete();
            }
        }
    }

    // 设置点击回掉监听
    private CustomerKeyboardClickListener mListener;
    public void setOnCustomerKeyboardClickListener(CustomerKeyboardClickListener listener){
        this.mListener = listener;
    }

    /**
     * 点击键盘的回调监听
     */
    public interface CustomerKeyboardClickListener {
        public void click(String number);
        public void delete();
    }
}

3.MainActivity

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.xinrui.dialog.CommonDialog;

public class MainActivity extends Activity implements CustomKeyboard.CustomerKeyboardClickListener, PsdEditText.PasswordFullListener, View.OnClickListener {

    private CustomKeyboard mCustomKeyboard;
    private PsdEditText mPsdEt;

    private TextView mTiptextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       /* mCustomKeyboard = (CustomerKeyboard) findViewById(R.id.customer_keyboard);
        mCustomKeyboard.setOnCustomerKeyboardClickListener(this);
        mPsdEt = (PasswordEditText) findViewById(R.id.password_et);
        mPsdEt.setEnabled(false);

        mPsdEt.setOnPasswordFullListener(this);*/
        mTiptextView = (TextView) findViewById(R.id.tip_txt);
        mTiptextView.setOnClickListener(this);
    }

    @Override
    public void click(String number) {
        mPsdEt.addPassword(number);
    }

    @Override
    public void delete() {
        mPsdEt.deleteLastPassword();
    }

    @Override
    public void passwordFull(String password) {
        // 显示进度条去后台校验密码
        Toast.makeText(this,"密码输入完毕->"+password,Toast.LENGTH_LONG).show();
    }

    @Override
    public void onClick(View v) {
        // 弹出dialog  从底部并且带动画
        CommonDialog.Builder builder = new CommonDialog.Builder(this);
        builder.setView(R.layout.customer_keyboard).fromBottom().fullWidth().create().show();
        mPsdEt =  builder.getView(R.id.password_edit_text);
        mCustomKeyboard = builder.getView(R.id.custom_key_board);
        mCustomKeyboard.setOnCustomerKeyboardClickListener(this);
        mPsdEt.setEnabled(false);
        mPsdEt.setOnPasswordFullListener(this);
    }
}

3.customer_keyboard.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:background="#f7f7f7"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_height="wrap_content">

        <ImageView
            android:padding="10dp"
            android:id="@+id/delete_dialog"
            android:layout_width="wrap_content"
            android:layout_centerVertical="true"
            android:src="@mipmap/delete_black"
            android:layout_height="wrap_content" />

        <TextView
            android:padding="15dp"
            android:layout_width="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="20sp"
            android:textColor="#444a59"
            android:text="输入密码"
            android:layout_height="wrap_content" />
    </RelativeLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.6dp"
        android:background="#d1d2d6"
        />

    <com.xinrui.PsdEditText
        android:id="@+id/password_edit_text"
        android:layout_width="match_parent"
        android:layout_marginTop="23dp"
        android:layout_marginRight="45dp"
        android:background="@null"
        android:padding="10dp"
        app:bgCorner="3dp"
        android:layout_marginLeft="45dp"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_height="wrap_content" />


    <com.xinrui.CustomKeyboard
        android:id="@+id/custom_key_board"
        android:layout_marginTop="23dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

ui_custom_keyboard.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#EBEBEB"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="1" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="2" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="3" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="4" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="5" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="6" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="7" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="8" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="9" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="1dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="20dp" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="1dp"
            android:layout_weight="1"
            android:background="#FFFFFF"
            android:gravity="center"
            android:padding="20dp"
            android:text="0" />

        <ImageView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:padding="15dp"
            android:layout_gravity="center_vertical"
            android:src="@drawable/customer_password_keyboard_delete" />
    </LinearLayout>
</LinearLayout>

CommonDialog

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ListView;

import com.xinrui.R;


public class CommonDialog extends Dialog {
    private AlertController mAlert;

    public CommonDialog(Context context, int theme) {
        super(context, theme);
        mAlert = new AlertController(getWindow(), this);
    }

    /**
     * Look for a child view with the given id. If this view has the given id,
     * return this view.
     * <p>
     * The id to search for.
     *
     * @return The view that has the given id in the hierarchy or null
     */
    public <T extends View> T getView(int viewId) {
        return mAlert.getView(viewId);
    }

    /**
     * Set the resource id of the {@link Drawable} to be used in the title.
     * <p>
     *
     * @return This Builder object to allow for chaining of calls to set methods
     */
    public void setIcon(int viewId, int resouceId) {
        mAlert.setIcon(viewId, resouceId);
    }

    /**
     * Set the {@link Drawable} to be used in the title.
     *
     * @return This Builder object to allow for chaining of calls to set methods
     */
    public void setIcon(int viewId, Drawable drawable) {
        mAlert.setIcon(viewId, drawable);
    }

    /**
     * Creates a {@link AlertDialog} with the arguments supplied to this builder
     * and {@link Dialog#show()}'s the com.hc.passwordedittext.dialog.
     */
    public void setText(int viewId, CharSequence text) {
        mAlert.setText(viewId, text);
    }

    /**
     * Creates a {@link AlertDialog} with the arguments supplied to this builder
     * and {@link Dialog#show()}'s the com.hc.passwordedittext.dialog.
     */
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mAlert.setOnClickListener(viewId, listener);
    }


    public static class Builder {
        private int mTheme;
        private final AlertController.AlertParams P;

        public Builder(Context context, int theme) {
            this.mTheme = theme;
            P = new AlertController.AlertParams(context);
        }

        public Builder(Context context) {
            this(context, R.style.dialog);
        }

        public CommonDialog create() {
            final CommonDialog dialog = new CommonDialog(P.mContext, mTheme);

            P.apply(dialog.mAlert);

            dialog.setCancelable(P.mCancelable);

            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }

            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }

        /**
         * Returns a {@link Context} with the appropriate theme for dialogs
         * created by this Builder. Applications should use this Context for
         * obtaining LayoutInflaters for inflating views that will be used in
         * the resulting dialogs, as it will cause views to be inflated with the
         * correct theme.
         *
         * @return A Context for built Dialogs.
         */
        public Context getContext() {
            return P.mContext;
        }

        /**
         * Set the resource id of the {@link Drawable} to be used in the title.
         * <p>
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setIcon(int viewId, int resouceId) {
            P.setIcon(viewId, resouceId);
            return this;
        }

        /**
         * Set the {@link Drawable} to be used in the title.
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setIcon(int viewId, Drawable icon) {
            P.setIcon(viewId, icon);
            return this;
        }

        /**
         * Sets whether the com.hc.passwordedittext.dialog is cancelable or not. Default is true.
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setCancelable(boolean cancelable) {
            P.mCancelable = cancelable;
            return this;
        }

        /**
         * Sets the callback that will be called if the com.hc.passwordedittext.dialog is canceled.
         *
         * <p>
         * Even in a cancelable com.hc.passwordedittext.dialog, the com.hc.passwordedittext.dialog may be dismissed for reasons
         * other than being canceled or one of the supplied choices being
         * selected. If you are interested in listening for all cases where the
         * com.hc.passwordedittext.dialog is dismissed and not just when it is canceled, see
         * {@link #setOnDismissListener(OnDismissListener)
         * setOnDismissListener}.
         * </p>
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         * @see #setCancelable(boolean)
         * @see #setOnDismissListener(OnDismissListener)
         */
        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
            P.mOnCancelListener = onCancelListener;
            return this;
        }

        /**
         * Sets the callback that will be called when the com.hc.passwordedittext.dialog is dismissed
         * for any reason.
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
            P.mOnDismissListener = onDismissListener;
            return this;
        }

        /**
         * Sets the callback that will be called if a key is dispatched to the
         * com.hc.passwordedittext.dialog.
         *
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
            P.mOnKeyListener = onKeyListener;
            return this;
        }

        /**
         * Set a custom view resource to be the contents of the Dialog. The
         * resource will be inflated, adding all top-level views to the screen.
         *
         * @param layoutResId Resource ID to be inflated.
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setView(int layoutResId) {
            P.mView = null;
            P.mViewLayoutResId = layoutResId;
            return this;
        }

        /**
         * Set a custom view to be the contents of the Dialog. If the supplied
         * view is an instance of a {@link ListView} the light background will
         * be used.
         *
         * @param view The view to use as the contents of the Dialog.
         * @return This Builder object to allow for chaining of calls to set
         * methods
         */
        public Builder setView(View view) {
            P.mView = view;
            P.mViewLayoutResId = 0;
            return this;
        }

        /**
         * Creates a {@link AlertDialog} with the arguments supplied to this
         * builder and {@link Dialog#show()}'s the com.hc.passwordedittext.dialog.
         */
        public Builder setText(int id, CharSequence text) {
            P.setText(id, text);
            return this;
        }

        /**
         * Creates a {@link AlertDialog} with the arguments supplied to this
         * builder and {@link Dialog#show()}'s the com.hc.passwordedittext.dialog.
         */
        public Builder setOnClickListener(int id, View.OnClickListener listener) {
            P.setOnClickListener(id, listener);
            return this;
        }

        /**
         * Dismiss this com.hc.passwordedittext.dialog, removing it from the screen. This method can be
         * invoked safely from any thread. Note that you should not override
         * this method to do cleanup when the com.hc.passwordedittext.dialog is dismissed, instead
         * implement that in {@link #onStop}.
         */
        public void dismiss() {
            P.dismiss();
        }

        /**
         * Look for a child view with the given id. If this view has the given
         * id, return this view.
         * <p>
         * The id to search for.
         *
         * @return The view that has the given id in the hierarchy or null
         */
        public <T extends View> T getView(int viewId) {
            return P.getView(viewId);
        }

        /**
         * Animation from the bottom of the pop-up to the middle all the time
         */
        public Builder fromBottomToMiddle() {
            P.fromBottomToMiddle();
            return this;
        }

        /**
         * Copy IOS pop-up effect
         */
        public Builder fromBottom() {
            P.fromBottom();
            return this;
        }

        /**
         * Special value for the height or width requested by a View.
         * MATCH_PARENT means that the view wants to be as big as its parent,
         * minus the parent's padding, if any. Introduced in API Level 8.
         */
        public Builder fullWidth() {
            P.setWidth(LayoutParams.MATCH_PARENT);
            return this;
        }

        /**
         * Loads a displacement of the default zoom animation
         */
        public Builder loadAniamtion() {
            P.loadAniamtion();
            return this;
        }

        /**
         * Information about how wide the view wants to be. Can be one of the
         * constants FILL_PARENT (replaced by MATCH_PARENT , in API Level 8) or
         * WRAP_CONTENT. or an exact size.
         */
        public Builder setWidth(int width) {
            P.setWidth(width);
            return this;
        }

        /**
         * Information about how tall the view wants to be. Can be one of the
         * constants FILL_PARENT (replaced by MATCH_PARENT , in API Level 8) or
         * WRAP_CONTENT. or an exact size.
         */
        public Builder setHeight(int height) {
            P.setHeight(height);
            return this;
        }
    }

}

你可能感兴趣的:(android)