首先看一下我的布局代码:
<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);
}