自定义控件:用户名输入框

自定义控件之用户名输入框

实现功能:

  1. 存在删除按钮,可以进行直接删除
  2. 存在展开下拉框按钮,可以展开保存过的用户名
  3. 实现焦点改变是边框颜色的改变

bug如下:欢迎大神帮忙改正

  1. 输入内容过长时,文字会显示在删除按钮之下
  2. 多行输入时,删除按钮被挤出可视范围
  3. 多行输入时,焦点边框被挤出可视范围

本文与之前的密码输入框是一套类型的控件,如有需要可参考如下:密码输入框

话不多说,看看具体代码吧,注释也很详细,就不在多解释了

package com.xxxx.Login;
package com.minimaxhk.Login;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;

import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.minimaxhk.adapter.RecycleViewItemDivider;
import com.minimaxhk.cmis.R;

import java.util.ArrayList;
import java.util.List;


/**
 * 登陆界面的用户名输入框
 * 可下拉,可删除,可输入
 *
 * @author Charlie
 */
public class LoginUserNameEditText extends AppCompatEditText {
    private       Context      context;
    /**
     * 底部线的默认颜色
     */
    private       int          downLineDefaultColor;
    /**
     * 底部线获取焦点颜色
     */
    private       int          downLineFocusColor;
    /**
     * 底部线的画笔
     */
    private       Paint        downLinePaint;
    /**
     * 清除图片的画笔
     */
    private       Paint        clearPaint;
    /***
     * 右侧要显示的图片
     */
    private       Drawable     iconRightDrawable;
    /***
     * 显示下拉列表的图标
     */
    private       Bitmap       iconRightContent;
    /**
     * 清除内容图标
     */
    private       Bitmap       iconClear;
    /***
     * 是否获取焦点
     */
    private       boolean      hasFocus;
    /***
     * 是否展开下拉列表
     */
    private       boolean      isShow  = false;
    /***
     * 画出的两个图的间距
     */
    private final int          PADDING = 50;
    /***
     * 下拉内容的显示区域
     */
    private       PopupWindow  popupWindow;
    /***
     * 存放要显示的内容
     */
    private       List<String> list    = new ArrayList<>();

    //定义一个接口
    interface OnClearContentListener {
        void onClick(int position);
    }

    /***
     * 定义一个接口对象
     */
    private OnClearContentListener onClearContentListener;

    /***
     * 删除条目监听
     */
    public void setOnClearContentListener(OnClearContentListener onClearContentListener) {
        this.onClearContentListener = onClearContentListener;
    }

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

    public LoginUserNameEditText(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.editTextStyle);
        this.context = context;
    }

    public LoginUserNameEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        initIcon();
    }

    private void initIcon() {
        downLineDefaultColor = getResources().getColor(R.color.down_line_default_color);
        downLineFocusColor = getResources().getColor(R.color.down_line_focus_color);
        if (isShow) {
            iconRightDrawable = getResources().getDrawable(R.drawable.ic_hint_user_names);
        } else {
            iconRightDrawable = getResources().getDrawable(R.drawable.ic_show_user_names);
        }
        iconRightContent = svgToBitmap(iconRightDrawable);
        iconClear = svgToBitmap(getResources().getDrawable(R.drawable.ic_clear_content));
        setBackground(null);
        initPaint();
    }

    private void initPaint() {
        downLinePaint = new Paint();
        downLinePaint.setColor(downLineDefaultColor);
        downLinePaint.setStrokeWidth(dp2px(1));
        downLinePaint.setAntiAlias(true);

        clearPaint = new Paint();
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        hasFocus = focused;
        if (!focused) {
            //失去焦点时
            isShow = false;
        }
        // 控件获取焦点时改变底部线的颜色
        downLinePaint.setColor(focused ? downLineFocusColor : downLineDefaultColor);
        invalidate();//更新视图
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isShow) {
            iconRightDrawable = getResources().getDrawable(R.drawable.ic_hint_user_names);
        } else {
            iconRightDrawable = getResources().getDrawable(R.drawable.ic_show_user_names);
        }
        //绘制右侧图标
        setCompoundDrawablesWithIntrinsicBounds(getCompoundDrawables()[0], getCompoundDrawables()[1], iconRightDrawable, getCompoundDrawables()[3]);
        //距离控件左侧的距离 ( 后面的数字是两个图片之间的间隔)
        int left = getWidth() - iconRightContent.getWidth() - iconClear.getWidth() - PADDING;
        //距离控件顶部的距离
        int top = (getHeight() - iconClear.getHeight()) / 2;
        if (hasFocus && getText().length() > 0) {
            //文字内容不为空时,显示删除图标
            canvas.drawBitmap(iconClear, left, top, clearPaint);
        }
        // 绘制底部的线
        canvas.drawLine(getScrollX(), this.getHeight() - dp2px(1), getScrollX() + this.getWidth(), this.getHeight() - dp2px(1), downLinePaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            int eventX = (int) event.getX();//触摸点左上角x坐标,相对于控件本身的坐标
            int eventY = (int) event.getY();
            //触摸按下时
            if (getCompoundDrawables()[2] != null) {
                //右侧存在图片
                // getTotalPaddingRight 图片左侧距离控件右边框的距离
                Boolean isInnerWidth = eventX > (getWidth() - getTotalPaddingRight() - PADDING / 2) && eventX < getWidth();
                Boolean isInnerHeight = eventY > 0 && eventY < getHeight();
                if (isInnerHeight && isInnerWidth) {
                    // 触摸在了显示内容的图标上
                    isShow = !isShow;
                    showPopupWindow();
                    invalidate();
                    return false;
                } else {
                    isShow = false;
                    if (popupWindow != null && popupWindow.isShowing()) {
                        popupWindow.dismiss();
                    }
                    invalidate();
                }
            }
            // 清空图片左侧距离控件左侧的距离
            int left = getWidth() - iconRightContent.getWidth() - iconClear.getWidth() - PADDING;
            Boolean isWidth = eventX > left && eventX < (getWidth() - iconRightContent.getWidth() - PADDING / 2);
            Boolean isHeight = eventY > 0 && eventY < getHeight();
            if (isWidth && isHeight) {
                //触摸在清空图片上
                setText("");
                invalidate();
                return false;
            }
        }
        return super.onTouchEvent(event);
    }

    private int sp2px(float spValue) {
        float spDensity = getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * spDensity + 0.5f);
    }

    /***
     * dp转成px
     * @param dip
     * @return
     */
    private int dp2px(double dip) {
        float dpDensity = getContext().getResources().getDisplayMetrics().density;
        return (int) (dip * dpDensity + 0.5);
    }

    /***
     * svg矢量图转成Bitmap
     * @param drawable
     * @return
     */
    private Bitmap svgToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /***
     * 打开显示内容区域
     */
    private void showPopupWindow() {
        if (popupWindow != null && !isShow) {
            popupWindow.dismiss();
            return;
        }
        View view = inflate(context, R.layout.layout_login_show_username, null);
        int height = getLayoutParams().height;
        if (list.size() > 0) {
            if (list.size() < 5) {
                popupWindow = new PopupWindow(view, getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
            } else {
                popupWindow = new PopupWindow(view, getWidth(), height * 5 + dp2px(20));
            }
        } else {
            popupWindow = new PopupWindow(view, getWidth(), getLayoutParams().height);
        }
        //这里必须设置为true才能点击区域外或者消失
        popupWindow.setOutsideTouchable(true);
        RecyclerView recyclerView = view.findViewById(R.id.recycle_login_show_username);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));
        RecycleViewItemDivider recycleViewItemDivider = new RecycleViewItemDivider(context, 1, R.color.tv_color_white);
        recyclerView.addItemDecoration(recycleViewItemDivider);
        recyclerView.setAdapter(new PopupWindowShowContentAdapter());
        popupWindow.setTouchInterceptor(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
                    //手指不在空间区域时触发
                    if (popupWindow != null && popupWindow.isShowing()) {
                        popupWindow.dismiss();
                        int[] coords = new int[2];
                        //获取用户名输入框左上角的绝对坐标coords[X,Y]
                        LoginUserNameEditText.this.getLocationOnScreen(coords);
                        int left = coords[0];//X坐标
                        int top = coords[1];//Y坐标
                        //右下角的坐标为(right,bottom)
                        int right = left + LoginUserNameEditText.this.getMeasuredWidth();//X坐标加上控件宽度就是右下角的X坐标
                        int bottom = top + LoginUserNameEditText.this.getMeasuredHeight();//Y坐标加上控件高度就是右下角的Y坐标
                        //整个控件所在的矩阵包含的坐标集合(相对于整个屏幕)
                        Rect rect = new Rect(left, top, right, bottom);
                        //获取触摸点的坐标(相对于整个屏幕)
                        int rawY = (int) motionEvent.getRawY();
                        int rawX = (int) motionEvent.getRawX();
                        if (!rect.contains(rawX, rawY)) {
                            //触摸的位置不在用户名输入框内
                            isShow = false;
                        }
                    }
                    return true;
                }
                return false;
            }
        });
        popupWindow.showAsDropDown(this);
    }

    /***
     * 设置下拉框要显示的内容
     * @param list
     */
    public void setShowContext(List<String> list) {
        this.list = list;
    }

    class PopupWindowShowContentAdapter extends RecyclerView.Adapter<ViewHolder> {

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(context).inflate(R.layout.item_login_show_username, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.textView.setText(list.get(position));
            holder.linearLayout.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    holder.linearLayout.setBackgroundColor(R.drawable.item_login_username_spinner_item_select_bg);
                    setText(list.get(position));
                    isShow = false;
                    popupWindow.dismiss();
                }
            });
            holder.imageView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (onClearContentListener != null) {
                        onClearContentListener.onClick(position);
                        isShow = false;
                        popupWindow.dismiss();
                    }
                }
            });
        }

        @Override
        public int getItemCount() {
            return list == null ? 0 : list.size();
        }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        LinearLayout linearLayout;
        TextView     textView;
        ImageView    imageView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            linearLayout = itemView.findViewById(R.id.ll_item_login_show_username);
            textView = itemView.findViewById(R.id.tv_login_show_username);
            imageView = itemView.findViewById(R.id.img_clear_username);
        }
    }
}

下拉框的布局如下:

<?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="@color/show_user_name_background">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycle_login_show_username"
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

recycleview条目布局如下

<?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/ll_item_login_show_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/show_user_name_background"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_login_show_username"
            style="@style/TextFontStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingVertical="15dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="@string/text_username_hint"
            android:textColor="@color/tv_color_black"
            android:textSize="@dimen/text_size_16sp" />

        <ImageView
            android:id="@+id/img_clear_username"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingHorizontal="5dp"
            app:srcCompat="@drawable/ic_clear_content" />
    </LinearLayout>


    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/tv_color_white" />
</LinearLayout>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java用户名密码验证实例,Java窗口程序,可以学习一下在窗体中如何初始化文本输入框、初始化密码输入框、登录按钮事件处理、初始化按钮事件处理等,并设定不允许用户改变窗口大小,运行效果如截图所示。   public class JPasswordFieldDemo extends JFrame {   JTextField username; //用户名输入框   JPasswordField password; //密码输入框   JButton logonButton; //登录按钮   JButton cancelButton; //退出按钮   public JPasswordFieldDemo() { //构造函数    super("JPasswordField演示"); //调用父类构造函数    Container container=getContentPane(); //得到容器    container.setLayout(new GridLayout(3, 2, 2, 2)); //设置布局管理器    username=new JTextField(16); //初始化文本输入框,宽度为16列    password=new JPasswordField(16); //初始化密码输入框,宽度为16列    logonButton=new JButton("登录"); //初始化登录按钮    logonButton.addActionListener( //登录按钮事件处理    new ActionListener(){    public void actionPerformed(ActionEvent evt){    char[] pw=password.getPassword(); //得到密码    String message="您的用户名:" username.getText() "\n您的密码:" new String(pw); //消息字符串    JOptionPane.showMessageDialog(JPasswordFieldDemo.this, message); //显示消息    }    });    cancelButton=new JButton("退出"); //初始化退出按钮    cancelButton.addActionListener( //初始化按钮事件处理    new ActionListener(){    public void actionPerformed(ActionEvent evt){    System.exit(0); //退出程序    }    });

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值