Android 自定义指示器<NBIndicatorView>

通过不间断的公司项目发现,在Android中使用透明指示器的地方很多.

以往自己都是通过继承dialog来实现的透明指示器,当然,为了研究更多东东.这些天特意写出了另外一种实现方式.

其实也没有什么特别之处,只是在看源码时偶然想到才实现的.

还是先上效果图片吧,有道是有图有真相:



上面就是效果截图,当然了,这里只涉及到了一种效果.说好了,只是为了研究,顺便分享.

从布局开始:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg_main"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onAction"
        android:text="孤独的指示器" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onAction"
        android:text="有背景的指示器" />

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onAction"
        android:text="有背景,有说明书的指示器" />

    <Button
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onAction"
        android:text="没有背景,有说明书的指示器" />

    <Button
        android:id="@+id/button5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onAction"
        android:text="只能自动消失的指示器" />

</LinearLayout>

布局很简单,就几个button而已,添加了事件.

然后,就是最主要的了,采用单例模式实现的指示器类,通过WindowManager实现的透明指示器类,如下

package com.napoleonbai.view;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.napoleonbai.activity.R;

/**
 * 自定义NBIndicatorView来实现弹出透明指示器
 * 
 * @author NapoleonBai
 *
 */
public class NBIndicatorView extends View {
	private NBIndicatorView(Context context) {
		super(context);
		mContext = context;
		initView();
	}

	private NBIndicatorView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	private NBIndicatorView(Context context, AttributeSet attrs,
			int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	/** 定义NBindicatorView单例对象 */
	private static NBIndicatorView mNBIndicatorView;

	private static Context mContext;

	/**
	 * 单例方法,构建NBIndicatorView对象
	 * 
	 * @param mContext
	 * @return
	 */
	private static NBIndicatorView instance(Context mContext) {
		if (mNBIndicatorView == null) {
			mNBIndicatorView = new NBIndicatorView(mContext);
		}
		return mNBIndicatorView;
	}

	/** 布局的内边距 */
	private final int LAYOUTPADDING = 50;
	/** 设置是否显示文字 缺省为false */
	private static boolean NBIsShowStr = false;
	/** 显示的提示文字 */
	private static String NBIndicatorStr = "";
	/** 指示器提示文字大小 */
	private static int NBIndicatorStrSize;
	/** 指示器提示文字颜色 缺省为黑色 */
	private static int NBIndicatorStrColor = Color.BLACK;
	/** 指示图片名称 */
	private static int NBIndicatorImageName = -1;
	/** 是否需要自定义指示器 缺省为false */
	private static boolean NBIsCustomIndicator = false;
	/** 背景资源ID */
	private static int NBLayoutResID = R.drawable.indicator_layout_corner;
	/** 是否允许触摸屏幕消失 */
	private static boolean NBIsClick = false;
	/** WindowManager对象 */
	private static WindowManager NBWindowManager;
	/** WindowManager.LayoutParams对象,用来设置对象参数 */
	private static android.view.WindowManager.LayoutParams NBLP;
	/** 指示器布局对象 */
	private static LinearLayout NBLinearLayout;
	/** 是否需要背景颜色 默认是需要 */
	private static boolean NBIsNeedBgColor = true;
	/** 定义进度条控件 */
	private static ProgressBar NBProgressBar;
	/** 透明指示器是否已经显示 缺省为false:没有显示 */
	private static boolean isShow = false;
	/** 提示文字控件 */
	private static TextView NBTextView;
	/** 布局参数设置对象 */
	private static LayoutParams NBLayoutParams;

	/**
	 * 初始化布局视图
	 */
	private void initView() {
		NBWindowManager = (WindowManager) mContext
				.getSystemService(Context.WINDOW_SERVICE);
		// 设置布局
		NBLinearLayout = new LinearLayout(mContext);
		NBLinearLayout.setOrientation(LinearLayout.VERTICAL);

		NBLinearLayout.setGravity(Gravity.CENTER);
		// 设置内边距,这里不提供自定义内边距
		NBLinearLayout.setPadding(LAYOUTPADDING, LAYOUTPADDING, LAYOUTPADDING,
				LAYOUTPADDING);

		NBLayoutParams = new LayoutParams(
				LinearLayout.LayoutParams.MATCH_PARENT,
				LinearLayout.LayoutParams.MATCH_PARENT);
		;
		NBLinearLayout.setLayoutParams(NBLayoutParams);

		// 创建进度条对象实例
		NBProgressBar = new ProgressBar(mContext);

		NBProgressBar.setLayoutParams(NBLayoutParams);

		NBLinearLayout.addView(NBProgressBar);
		NBTextView = new TextView(mContext);
		NBTextView.setLayoutParams(NBLayoutParams);
		NBTextView.setSingleLine();
		NBTextView.setGravity(Gravity.CENTER_HORIZONTAL);
		NBLinearLayout.addView(NBTextView);
		// 如果有文字提示才设置该控件,否则不设置
		if (NBIsShowStr) {
			NBTextView.setVisibility(GONE);
		}
		// 设置WindowManager的参数
		NBLP = new android.view.WindowManager.LayoutParams(
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
				WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON,
				WindowManager.LayoutParams.FORMAT_CHANGED);
	}

	/**
	 * 定义的触摸事件,用于点击屏幕,指示器消失
	 */
	private static OnTouchListener NBOntouchListener = new OnTouchListener() {
		@Override
		public boolean onTouch(View view, MotionEvent event) {
			dismiss();
			return true;
		}
	};

	/**
	 * 设置其他参数
	 * 
	 * @param isCustomImage
	 *            是否需要自定义指示器
	 * @param indicatorImage
	 *            指示器图片Res eg:R.drawable.ic_luncher,如果isCustomImage=false,就设置为-1
	 */
	public static void setIndicatorImage(boolean isCustomImage,
			int indicatorImage) {
		NBIsCustomIndicator = isCustomImage;
		if (NBIndicatorImageName != indicatorImage) {
			NBIndicatorImageName = indicatorImage;
		}

	}

	/**
	 * 设置是否需要显示提示文字
	 * 
	 * @param isShowStr
	 */
	public static void setIsShowStr(boolean isShowStr) {
		NBIsShowStr = isShowStr;
		updateTextView();
	}

	/**
	 * 设置提示文字
	 * 
	 * @param str
	 */
	public static void setStr(String str) {
		NBIndicatorStr = str;
		updateTextView();
	}

	/**
	 * 更新提示文字
	 */
	private static void updateTextView() {
		if (NBTextView != null) {
			NBTextView.setText(NBIndicatorStr);
		}
	}

	/**
	 * 通过传入的资源ID来获取提示文字
	 * 
	 * @param strResId
	 */
	public static void setStr(int strResId) {
		NBIndicatorStr = mContext.getResources().getString(strResId);
	}

	/**
	 * 设置提示文字大小
	 * 
	 * @param strSize
	 */
	public static void setStrSize(int strSize) {
		NBIndicatorStrSize = strSize;
	}

	/**
	 * 
	 * @param strColor
	 *            颜色值,eg:Color.BLACK
	 */
	public static void setStrColor(int strColor) {
		NBIndicatorStrColor = strColor;
	}

	/**
	 * 
	 * 是否需要背景颜色
	 * 
	 * @param isNeedBgColor
	 *            true or false:true为需要,缺省为true
	 * @param bgResId
	 *            设置isNeedBgColor=true的时候,调用此方法 true:设置引用的drawable
	 *            ID<drawable可参照R.drawable
	 *            .indicator_layout_corner.xml.当然如果想自定义背景
	 *            ,那么就传入自己写的那个,否则,使用R.drawable .indicator_layout_corner.xml>
	 */

	public static void setIsNeedBgColor(boolean isNeedBgColor, int bgResId) {
		NBIsNeedBgColor = isNeedBgColor;
		if (NBIsNeedBgColor) {
			NBLayoutResID = bgResId;
		}
	}

	/**
	 * 是否需要背景颜色
	 * 
	 * @param isNeedBgColor
	 *            设置为false的时候调用
	 */
	public static void setIsNeedBgColor(boolean isNeedBgColor) {
		NBIsNeedBgColor = isNeedBgColor;
	}

	/**
	 * 设置点击屏幕消失,设置true,点击消失,反之则不能点击消失
	 * 
	 * @param isDismiss
	 */
	public static void setClickScreenDismiss(boolean isClick) {
		NBIsClick = isClick;
	}

	/**
	 * 根据设置好的信息设置需要参数
	 */
	private static void setParmas() {
		if (NBIsNeedBgColor) {
			// 设置圆角边框,默认设置的,毋须更改,更改请移步到R.drawable.indicator_layout_corner
			NBLinearLayout.setBackgroundResource(NBLayoutResID);
		} else {
			NBLinearLayout.setBackgroundResource(Color.TRANSPARENT);
		}
		if (NBIsShowStr) {
			// 这里这样判断,主要是为了支持不同的显示方式之间的切换,下同
			NBTextView.setVisibility(View.VISIBLE);
			NBTextView.setText(NBIndicatorStr);
			NBTextView.setTextColor(NBIndicatorStrColor);
		} else {
			NBTextView.setVisibility(View.GONE);
		}

		if (NBIndicatorStrSize > 0) {
			NBTextView.setTextSize(NBIndicatorStrSize);
		}

		if (NBIsCustomIndicator) {
			NBProgressBar.setIndeterminateDrawable(mContext.getResources()
					.getDrawable(NBIndicatorImageName));
		}
		if (NBIsClick) {
			NBLinearLayout.setOnTouchListener(NBOntouchListener);
		}
	}

	/**
	 * 提供show()方法,设置显示指示器
	 */
	public static void show(Context mContext) {
		if (!isShow) {
			instance(mContext);
			setParmas();
			NBWindowManager.addView(NBLinearLayout, NBLP);
			isShow = true;
		}
	}

	/**
	 * 移除透明指示器方法:dismiss()
	 */
	public static void dismiss() {
		if (isShow) {
			NBWindowManager.removeView(NBLinearLayout);
			NBIsShowStr = false;// 每次重置,恢复到缺省值
			NBIsCustomIndicator = false;
			NBIsNeedBgColor = true;
			isShow = false;
		}
	}
}

在代码中,说明我已经指出了,所以,过多的文字叙述,我觉得就不必要了.

现在的话,应该指出上面提到的几个drawable文件,首先,是我们的自定义圆角背景:indicator_layout_corner.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#77FFFFFF" />

    <corners android:radius="10dp" />

</shape>

这个文件主要就是填充指示器的背景,这里设置为半透明状,可根据自身需要进行修改;

然后就是指示器,指示器采用的是ProgressBar,所以这里就很简单的进行了处理了一下:

1.nb_loading_main.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" >

    <shape
        android:innerRadiusRatio="3"
        android:shape="ring"
        android:thicknessRatio="8"
        android:useLevel="false" >
        <gradient
            android:centerColor="#FF00FFFF"
            android:centerY="0.50"
            android:endColor="#FFFFFF00"
            android:startColor="#FFFF0000"
            android:type="sweep"
            android:useLevel="false" />
    </shape>

</rotate>

上面这个文件主要是以修改进度条颜色来实现,上述图片就是采用的这个xml来实现;

2.nb_loading_main1.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/loading_icon"
    android:pivotX="50%"
    android:pivotY="50%" />

main1文件主要是通过对loading_icon图片的旋转来进行实现

当然了,这里提供的两种方式皆可.

然后再看MainActivity类中的实现吧.这个类指出来怎样调用,怎样设置

package com.napoleonbai.activity;

import com.napoleonbai.view.NBIndicatorView;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.View;

/**
 * 测试界面类
 * 
 * @author NapoleonBai
 *
 */
public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initNBIndicatorView();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * 进行初始化设置 因为指示器采用的是单例模式,所以很多相同的设置都可以在同一个地方进行一并设置,不需要重复设置
	 */
	private void initNBIndicatorView() {
		// 如果不设置,就采用系统的默认样式
		NBIndicatorView.setIndicatorImage(true, R.drawable.nb_loading_main);
		// 这是另外一种
		// NBIndicatorView.setIndicatorImage(true, R.drawable.nb_loading_main1);
	}

	/**
	 * 按钮点击事件方法
	 * 
	 * @param v
	 */
	public void onAction(View v) {
		// 点击外部可以取消,如果不设置,则不能取消
		NBIndicatorView.setClickScreenDismiss(true);
		switch (v.getId()) {
		case R.id.button1:
			// 不需要显示背景颜色
			NBIndicatorView.setIsNeedBgColor(false);
			NBIndicatorView.show(this);
			break;
		case R.id.button2:
			NBIndicatorView.show(this);
			break;
		case R.id.button3:
			NBIndicatorView.setStrColor(Color.BLUE);
			// 需要显示提醒文字
			NBIndicatorView.setIsShowStr(true);
			NBIndicatorView.setStr("关爱小白杨");
			NBIndicatorView.show(this);
			break;
		case R.id.button4:
			NBIndicatorView.setStrColor(Color.GREEN);
			NBIndicatorView.setStr("爱护小白杨");
			NBIndicatorView.setIsShowStr(true);
			// 不需要显示背景颜色
			NBIndicatorView.setIsNeedBgColor(false);
			NBIndicatorView.show(this);
			break;
		case R.id.button5: {
			// 点击外部,不能取消指示器
			NBIndicatorView.setClickScreenDismiss(false);
			NBIndicatorView.setStr("准备好:马上就开始了");
			NBIndicatorView.setIsShowStr(true);
			NBIndicatorView.setStrColor(Color.MAGENTA);
			NBIndicatorView.show(this);
			handler.postDelayed(runnable, TIME); // 每隔2s执行
		}
			break;

		default:
			break;
		}
	}

	// 显示文字
	private String[] strDatas = new String[] { "小白:你知道吗?", "小杨:什么?",
			"小白:小白杨很可爱的", "小杨:真的吗?", "小白:我们回去养一个吧!", "小杨:小白杨=小白羊?",
			"小白:喜羊羊/懒羊羊?", "小杨:......", "希望大家都能有收获", "谢谢", "马上就消失了哟", "走了,拜拜" };
	// 下标
	private int i = 0;
	private int TIME = 2000;
	Handler handler = new Handler();
	Runnable runnable = new Runnable() {
		@Override
		public void run() {
			// handler自带方法实现定时器
			try {
				handler.postDelayed(this, TIME);
				if (i >= strDatas.length) {
					NBIndicatorView.dismiss();// 移除指示器
					handler.removeCallbacks(runnable);// 停止计时器
					return;
				}
				NBIndicatorView.setStr(strDatas[i]);
				i++;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};
}

上述代码已经给出了比较详细的注释,也说明了怎样调用.因为是采用单例模式实现,所以在同一个项目中最好只采用一种颜色的指示器.

当然了,再次说明,只为大家共同研究,一起讨论.

附上demo地址:http://a.code4app.com/android/NBIndicatorView/544b3236933bf07e238b536d




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值