自己总结了一下别人作品自定义view和弹窗选择功能

本文详细介绍了自定义Android View实现时间线标记视图,包括属性设置、测量与绘制流程,以及通过Activity、PopupWindow和Dialog三种方式实现功能弹窗的代码示例。

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

首先看一下我的布局代码:

<com.bt.mylibrary.TimeLineMarkerView
        android:id="@+id/tv_rv_activity_orderdetail_timelineview"
        android:layout_width="80dp"
        android:layout_height="100dp"
        app:beginLine="@color/blue_sky"
        app:endLine="@color/blue_sky"
        app:marker="@drawable/timeline_bg_blue"
        app:lineSize="1dp"
        app:markerSize="9dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

这个就是自定义的一个view,下面这个是效果图。
在这里插入图片描述
现在是重点实现代码TimeLineMankerVIew.java:
自定义的VIew继承自超类View

public class TimeLineMarkerView extends View {

    private int mMarkerSize = 24; //圆点大小,单位为dp
    private int mLineSize = 2; //线段粗细
    private Drawable mBeginLine; //上面线段颜色或者图片
    private Drawable mEndLine; //下面线段颜色或者图片
    private Drawable mMarkerDrawable;//圆点颜色或者图片
    private boolean  oritation; //竖向还是横向  false竖向,true横向
    public TimeLineMarkerView(Context context) {
        this(context,null);
    }

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

    public TimeLineMarkerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, com.bt.mylibrary.R.styleable.TimeLineMarker);

        mMarkerSize = a.getDimensionPixelSize(
                com.bt.mylibrary.R.styleable.TimeLineMarker_markerSize,
                mMarkerSize);

        mLineSize = a.getDimensionPixelSize(
                com.bt.mylibrary.R.styleable.TimeLineMarker_lineSize,
                mLineSize);

        mBeginLine = a.getDrawable(
                com.bt.mylibrary.R.styleable.TimeLineMarker_beginLine);

        mEndLine = a.getDrawable(
                com.bt.mylibrary.R.styleable.TimeLineMarker_endLine);
        mMarkerDrawable = a.getDrawable(
                com.bt.mylibrary.R.styleable.TimeLineMarker_marker);

        oritation= a.getBoolean(
                com.bt.mylibrary.R.styleable.TimeLineMarker_oritation,false);
        a.recycle();

        if (mBeginLine != null)
            mBeginLine.setCallback(this);

        if (mEndLine != null)
            mEndLine.setCallback(this);

        if (mMarkerDrawable != null)
            mMarkerDrawable.setCallback(this);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSpecSize= MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize= MeasureSpec.getSize(heightMeasureSpec);
        int widthSpecMode= MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode= MeasureSpec.getMode(heightMeasureSpec);
        if(oritation){
            if(widthSpecMode== MeasureSpec.AT_MOST && heightSpecMode== MeasureSpec.AT_MOST){
                setMeasuredDimension(120,80);//针对wrap情况做处理
            }else if(widthSpecMode== MeasureSpec.AT_MOST ){
                setMeasuredDimension(120,heightSpecSize);
            }else if(heightSpecMode== MeasureSpec.AT_MOST ){
                setMeasuredDimension(widthSpecSize,80);
            }
        }else{
            if(widthSpecMode== MeasureSpec.AT_MOST && heightSpecMode== MeasureSpec.AT_MOST){
                setMeasuredDimension(80,120);//针对wrap情况做处理
            }else if(widthSpecMode== MeasureSpec.AT_MOST ){
                setMeasuredDimension(80,heightSpecSize);
            }else if(heightSpecMode== MeasureSpec.AT_MOST ){
                setMeasuredDimension(widthSpecSize,120);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBeginLine != null) {
            mBeginLine.draw(canvas);
        }
        if (mEndLine != null) {
            mEndLine.draw(canvas);
        }
        if (mMarkerDrawable != null) {
            mMarkerDrawable.draw(canvas);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        initDrawableSize();
    }

    private void initDrawableSize() {
        int pLeft=getPaddingLeft();
        int pRight=getPaddingRight();
        int pTop=getPaddingTop();
        int pBottom=getPaddingBottom();
        int width=getWidth();
        int height=getHeight();
        int cWidth=width-pLeft-pRight;
        int cHeight=height-pTop-pBottom;
        Rect bounds;
        int mMarkerSizepx= ResourceUtil.dip2px(getContext(),mMarkerSize);
        int mLineSizepx=ResourceUtil.dip2px(getContext(),mLineSize);
        //横向
        if(oritation){
            if(mMarkerDrawable!=null){
                int marksize= Math.min(Math.min(cWidth,cHeight), mMarkerSizepx);
                mMarkerDrawable.setBounds(pLeft+width/2-marksize/2,pTop,pLeft+width/2-marksize/2+marksize,pTop+marksize);
                bounds=mMarkerDrawable.getBounds();
            }else{
                bounds=new Rect(pLeft+width/2,pTop,pLeft+width/2,pTop);
            }
            int halfLine=mLineSizepx >> 1;
            int linetop=bounds.centerY()-halfLine;
            if(mBeginLine!=null){
                mBeginLine.setBounds(0,linetop,bounds.left,linetop+mLineSizepx);
            }
            if(mEndLine!=null){
                mEndLine.setBounds(bounds.right,linetop,width,linetop+mLineSizepx);
            }
            //竖向
        }else{
            if(mMarkerDrawable!=null){
                int marksize= Math.min(Math.min(cWidth,cHeight), mMarkerSizepx);
                mMarkerDrawable.setBounds(pLeft,pTop+height/2-marksize/2,pLeft+marksize,pTop+height/2-marksize/2+marksize);
                bounds=mMarkerDrawable.getBounds();
            }else{
                bounds=new Rect(pLeft+mLineSizepx/2,pTop+height/2,pLeft+mLineSizepx/2,pTop+height/2);
            }
            int halfLine=mLineSizepx >> 1;
            int lineLeft=bounds.centerX()-halfLine;
            if(mBeginLine!=null){
                mBeginLine.setBounds(lineLeft,0,lineLeft+mLineSizepx,bounds.top);
            }
            if(mEndLine!=null){
                mEndLine.setBounds(lineLeft,bounds.bottom,lineLeft+mLineSizepx,height);
            }
        }
    }
       //下来提供几个方法。以供代码动态设置
    public void setLineSize(int  linesize) {
        if (this.mLineSize != linesize) {
            mLineSize = linesize;
            initDrawableSize();
            invalidate();
        }
    }

    public void setMarkerSize(int markerSize) {
        if (this.mMarkerSize != markerSize) {
            mMarkerSize = markerSize;
            initDrawableSize();
            invalidate();
        }
    }

    public void setBeginLine(Drawable beginLine) {
        if (this.mBeginLine != beginLine) {
            this.mBeginLine = beginLine;
            if (mBeginLine != null) {
                mBeginLine.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }

    public void setEndLine(Drawable endLine) {
        if (this.mEndLine != endLine) {
            this.mEndLine = endLine;
            if (mEndLine != null) {
                mEndLine.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }

    public void setMarkerDrawable(Drawable markerDrawable) {
        if (this.mMarkerDrawable != markerDrawable) {
            this.mMarkerDrawable = markerDrawable;
            if (mMarkerDrawable != null) {
                mMarkerDrawable.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }
    }

很明显可以看出来创建了一个继承View的Java类,首先,设置了3个构造函数,然后调用初始化属性的方法,其实就是设置好相对应的圆点大小,颜色,横向竖向等的view信息。最后是使用setCallback()的方法来进行设置,因为drawable的绘画是必须依赖于View实现的。

接下来是重写onMeasure()方法对横向和竖向都使用 setMeasuredDimension()方法设置相对应的视图大小,目的是把View放置到父容器中。

再下一步重写OnDraw()方法来进行视图的绘制。

最后一步重写onSizeChanged()方法,目的是在控件大小改变时进行调用,再初始化一次。

动态设置方法

public void setLineSize(int  linesize) {//自定义线段粗细
        if (this.mLineSize != linesize) {
            mLineSize = linesize;
            initDrawableSize();
            invalidate();
        }
    }

2.`

public void setBeginLine(Drawable beginLine) {//上字段颜色或图片
        if (this.mBeginLine != beginLine) {
            this.mBeginLine = beginLine;
            if (mBeginLine != null) {
                mBeginLine.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }
3.
 public void setMarkerSize(int markerSize) {//自定义圆点大小
        if (this.mMarkerSize != markerSize) {
            mMarkerSize = markerSize;
            initDrawableSize();
            invalidate();
        }
    }
public void setEndLine(Drawable endLine) {//下字段颜色或图片
        if (this.mEndLine != endLine) {
            this.mEndLine = endLine;
            if (mEndLine != null) {
                mEndLine.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }
public void setMarkerDrawable(Drawable markerDrawable) {//设置圆点颜色或图片
        if (this.mMarkerDrawable != markerDrawable) {
            this.mMarkerDrawable = markerDrawable;
            if (mMarkerDrawable != null) {
                mMarkerDrawable.setCallback(this);
            }
            initDrawableSize();
            invalidate();
        }
    }

以上5个方法可供在控制代码中调用来进行动态设置。

CustomDialog(功能弹窗)

在这里插入图片描述
这个项目转载自:https://github.com/crazyqiang/DownCustomDialog
在这里插入图片描述
项目当中有3中弹窗方式:分别是从POPUPwindow、Activity、Dialog。
下面将分别介绍这3种方式:
1.Activity启动:

在MainActivity当中:

case R.id.btn_activity:
  startActivity(new Intent(this, ActivityAsPopupwindow.class));//从activity开启
  break;
`


然后是ActivityAsPopupwindow.java:

public class ActivityAsPopupwindow extends AppCompatActivity implements View.OnClickListener {
private Button btn_take_photo, btn_select_photo, btn_cancel;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.popwindow_from_custom);
    initViews();
    initEvents();
    WindowManager m = getWindow().getWindowManager();
    Display d = m.getDefaultDisplay();
    WindowManager.LayoutParams p = getWindow().getAttributes();
    p.width = d.getWidth();
    getWindow().setAttributes(p);
}


private void initEvents() {
    //添加按钮监听
    btn_cancel.setOnClickListener(this);
    btn_take_photo.setOnClickListener(this);
    btn_select_photo.setOnClickListener(this);
}

private void initViews() {
    btn_take_photo = (Button) findViewById(R.id.btn_take_photo);
    btn_select_photo = (Button) findViewById(R.id.btn_select_photo);
    btn_cancel = (Button) findViewById(R.id.btn_cancel);
}

//实现onTouchEvent触屏函数但点击屏幕时销毁本Activity
@Override
public boolean onTouchEvent(MotionEvent event) {
    finish();
    return true;
}


@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn_take_photo:
            CommonUtil.toast("btn_take_photo From Activity");
            finish();
            break;
        case R.id.btn_select_photo:
            CommonUtil.toast("btn_select_photo From Activity");
            finish();
            break;
        case R.id.btn_cancel:
            CommonUtil.toast("cancel From Activity");
            finish();
            break;
    }
}

}
``
在这里首先初始化3个按钮的属性和点击事件。重写了onTouchEvent()方法
用于点击屏幕就销毁Activity。
最关键的在于使用WindowManager类和 Display类来进行窗口的设置成弹窗模式。

第二种方式:PopWindow

先看基类BasePopupWindow:

public abstract class BasePopupWindow extends PopupWindow {
    protected View mPopupView;
    protected Activity mContext;
    protected Animation showAnimation, dismissPopupWindow;

    public BasePopupWindow() {
        super();
    }

    public BasePopupWindow(Activity context) {
        initSet(context, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    }

    public BasePopupWindow(Activity context, int width, int height) {
        initSet(context, width, height);
    }

    private void initSet(Activity context, int width, int height) {
        mContext = context;
        mPopupView = getPopupWindow();
//        mPopupView.setFocusableInTouchMode(true);
        //实例化一个ColorDrawable颜色为半透明
        ColorDrawable cd = new ColorDrawable(0xb0000000);
        setContentView(mPopupView);
        this.setWidth(width);
        this.setHeight(height);
        this.setBackgroundDrawable(cd);
        this.setOutsideTouchable(true);
        this.setFocusable(true);
//        setAnimationStyle(R.style.AnimBottom);
        showAnimation = getShowAnimation();
        dismissPopupWindow = getDissmissAnimation();
    }

    protected abstract View getPopupWindow();

    protected abstract Animation getShowAnimation();

    protected abstract Animation getDissmissAnimation();

    public View findViewById(int id) {
        return mPopupView.findViewById(id);
    }

    public void showPopupWindow() {
        showAtLocation(mContext.findViewById(android.R.id.content), Gravity.BOTTOM, 0, 0);
        if (showAnimation != null) {
            mPopupView.clearAnimation();
            mPopupView.startAnimation(showAnimation);
        }
    }

    public void dismissPopupWindow() {
        dismiss();
        if (dismissPopupWindow != null) {
            mPopupView.clearAnimation();
            mPopupView.startAnimation(dismissPopupWindow);
        }
    }
}

该类继承自PopupWindow,目的是用于设置一个半透明的提示窗口,并且封装好显示方法和隐藏方法

具体实现代码:PopupWindowFromDown.java

该类继承PopupWindow类,关键是初始化时候为mMenuView添加OnTouchListener监听判断获取触屏位置如果在选择框外面则销毁弹出框

pop_layout.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    dismissPopupWindow();
                }
                return true;
            }
        });

点击方法onClick()类似与Activity的方法。

接下来重写了getPopupWindow()、getShowAnimation()和getDissmissAnimation()方法。

@Override
    protected View getPopupWindow() {
        view = LayoutInflater.from(mContext).inflate(R.layout.popwindow_from_custom, null);
        return view;
    }

    @Override
    protected Animation getShowAnimation() {
        return AnimationUtil.getTranslateAnimation(0, 0, 400, 0, 200);
    }

    @Override
    protected Animation getDissmissAnimation() {
        return null;
    }

在这里使用了Animation类的方法进行了一个动画的处理。

第三种方式:Dialog

直接上代码DownDialog.java:

public class DownDialog extends Dialog implements View.OnClickListener {
    private Button btn_take_photo, btn_select_photo, btn_cancel;

    public DownDialog(Context context) {
        this(context, R.style.option_dialog);
    }

    public DownDialog(Context context, int themeResId) {
        super(context, themeResId);
        View view = LayoutInflater.from(context).inflate(R.layout.popwindow_from_custom, null);
        initView(view);
        initListener();
        setContentView(view);
        getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
//                if(mDialogClickListener!=null){
//                    mDialogClickListener.onClick(R.id.btn_cancel);
//                }
                dismiss();
                return true;
            }
        });
    }

    private void initListener() {
        btn_cancel.setOnClickListener(this);
        btn_take_photo.setOnClickListener(this);
        btn_select_photo.setOnClickListener(this);
    }

    private void initView(View view) {
        btn_take_photo = (Button) view.findViewById(R.id.btn_take_photo);
        btn_select_photo = (Button) view.findViewById(R.id.btn_select_photo);
        btn_cancel = (Button) view.findViewById(R.id.btn_cancel);
    }

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        getWindow().setGravity(Gravity.BOTTOM);
        WindowManager m = getWindow().getWindowManager();
        Display d = m.getDefaultDisplay();
        WindowManager.LayoutParams p = getWindow().getAttributes();
        p.width = d.getWidth();
        getWindow().setAttributes(p);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_take_photo:
                CommonUtil.toast("btn_take_photo From Dialog");
                break;
            case R.id.btn_select_photo:
                CommonUtil.toast("btn_select_photo From Dialog");
                break;
            case R.id.btn_cancel:
                CommonUtil.toast("cancel From Dialog");
                break;
        }
        dismiss();
    }

}

很明显看出自定义一个继承自Dialog,使用的是自定义好的style,目的是设置为半透明,具体的style类可自己查,也是重写了onTouch方法能够点击屏幕从而达到销毁窗口的目的。在最重要的onCreate()方法当中,使用的类似于Activity打开的方法:

protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        getWindow().setGravity(Gravity.BOTTOM);
        WindowManager m = getWindow().getWindowManager();
        Display d = m.getDefaultDisplay();
        WindowManager.LayoutParams p = getWindow().getAttributes();
        p.width = d.getWidth();
        getWindow().setAttributes(p);
    }

结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值