现在的位置: 首页 > 综合 > 正文

android的一些自定义控件

2014年03月09日 ⁄ 综合 ⁄ 共 11018字 ⁄ 字号 评论关闭

1.toast

Toast toast = Toast.makeText(this, "自定义的toast", Toast.LENGTH_SHORT);
		toast.setGravity(Gravity.TOP, 30, 30);
		View view = LayoutInflater.from(this).inflate(R.layout.toast, null);
		toast.setView(view);
		toast.show();

2.Notification

mNotify = new Notification(R.drawable.icon, "通知", System.currentTimeMillis());
		// 正在运行,不允许被清除
		mNotify.flags = Notification.FLAG_ONGOING_EVENT;
                 //震动
		mNotify.defaults |= Notification.DEFAULT_VIBRATE;
		 long[] vibrates = new long[]{0, 100, 200, 300};
		 mNotify.vibrate = vibrates ;

声明震动权限<uses-permission android:name="android.permission.VIBRATE"></uses-permission>   注意模拟器没有震动功能,需要真机测试

// 定制intent
		Intent intent = new Intent(this, MyService.class);
		PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent,
				PendingIntent.FLAG_UPDATE_CURRENT);
                mNotify.contentIntent = pendingIntent;
// 自定义通知布局
		RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.updatenotify);
		mNotify.contentView = contentView;
// 使用通知
mNotifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		mNotifyMgr.notify(R.string.hello, mNotify);

//更新通知
mNotify.contentView.setProgressBar(R.id.progressBar1, NOTITY_PROGRESS_MAX, progress, false);
		mNotifyMgr.notify(R.string.hello, mNotify);
		if (progress == NOTITY_PROGRESS_MAX) {
			Toast.makeText(this, "下载完成,请查看通知安装", Toast.LENGTH_SHORT).show();
		}

3.dialog

protected Dialog onCreateDialog(int id) {
		Dialog dialog = new Dialog(this);
		dialog.setContentView(R.layout.dialog);
		dialog.setTitle("标题");
		return dialog;
	}
//在需要显示的地方写
showDialog(1);

4.menu

自定义菜单,分析菜单的特征

什么条件下显示: 按Menu

什么条件下关闭:
1) 当菜单显示时,再点击一次菜单按钮
2) 某一个菜单项被点击
3) back  Activity不响应back
4) 其它Activity激活
5) 点击菜单和状态栏以外的区域
补充:当菜单存在时,点击菜单以外区域,界面不能响应用户操作

public class MainActivity extends Activity implements OnClickListener {
	private PopupWindow mOptionsMenu;
	private int[] menuItemIDs = new int[] { R.id.menuitem1, R.id.menuitem2, R.id.menuitem3,
			R.id.menuitem4, R.id.menuitem5, R.id.menuitem6, R.id.menuitem7, R.id.menuitem8,
			R.id.empty };

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		View contentView = LayoutInflater.from(this).inflate(R.layout.optionsmenu, null);
		mOptionsMenu = new PopupWindow(contentView, LayoutParams.FILL_PARENT,
				LayoutParams.WRAP_CONTENT);
		initMenuItem(contentView);
	}

	@Override
	protected void onPause() {
		super.onPause();
		closeMenuIfExist();
	}

	private boolean closeMenuIfExist() {
		if (mOptionsMenu.isShowing()) {
			mOptionsMenu.dismiss();
			return true;
		}
		return false;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (closeMenuIfExist()) {
				return true;
			}
		}
		return super.onKeyDown(keyCode, event);
	}

	private void initMenuItem(View contentView) {
		for (int i = 0; i < menuItemIDs.length; i++) {
			contentView.findViewById(menuItemIDs[i]).setOnClickListener(this);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		if (!closeMenuIfExist()) {
			mOptionsMenu.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM, 0, 0);
		}
		return false;
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.menuitem1:
			Intent intent = new Intent();
			intent.setClass(this, Second.class);
			startActivity(intent);
			break;
		case R.id.menuitem2:

			break;
		case R.id.menuitem3:
			break;
		case R.id.menuitem4:
			break;
		case R.id.menuitem5:
			break;
		case R.id.menuitem6:
			break;
		case R.id.menuitem7:
			break;
		case R.id.menuitem8:
			break;
		case R.id.empty:
			break;

		default:
			break;
		}
		mOptionsMenu.dismiss();
	}

}

注意,R.id.empty指的是菜单外面的view,可以用帧布局放置
可以用selector 为菜单设置点击效果
菜单项显示图片和文本
两类:
1) 图片和文本做成一张图片
2) GridView
   布局嵌套  

5.progress

<SeekBar
                android:id="@+id/progress"
                style="?android:attr/progressBarStyleHorizontal"
                android:progressDrawable="@drawable/seek_background"
                android:thumb="@drawable/thumb"
                android:layout_width="346dip"
                android:layout_height="32dip"
                android:maxHeight="10dip"
                android:paddingLeft="8dip"
                android:paddingRight="8dip"
                android:paddingTop="2dip"
                android:paddingBottom="6dip"
                android:max="1000"/>
<!--                android:minHeight="10dip"-->
seek_background.xml
<?xml version="1.0" encoding="UTF-8"?>
<layer-list
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background" android:drawable="@drawable/time_line_bg" />
    <item android:id="@android:id/progress" android:drawable="@drawable/progress_time" />
</layer-list>

tumb.xml

<?xml version="1.0" encoding="UTF-8"?>
<selector
	xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- 按下状态 -->
	<item
		android:state_pressed="true"
		android:drawable="@drawable/drag_btn_down" />

	<!-- 普通无焦点状态 -->
	<item
		android:state_focused="false"
		android:state_pressed="false"
		android:drawable="@drawable/drag_btn" />
</selector>

6.view

public class LabelView extends View {
	private Paint mTextPaint;
	private String mText;
	private int mAscent;

	/**
	 * 定义一个构造器来初始化这个自定义的view,这个构造器可以在java代码中用来生成view 如 new LabelView(context)
	 * 
	 * @param context
	 *            上下文
	 */
	public LabelView(Context context) {
		super(context);
		initLabelView();
	}

	/**
	 * @param context
	 *            上下文
	 * @param attrs
	 *            从带styleable的xml中读到的属性
	 */
	public LabelView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initLabelView();
		// 从R.styleable.LabelView文件中获得属性集合
		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabelView);
		// 通过typedArray.getString或者typedArray.getInt获得对应的属性设定的值
		// 属性的名称为styleable名称“LabelView”,加上“_”,再加上属性名称"text",组成“LabelView_text”
		CharSequence s = a.getString(R.styleable.LabelView_text);
		if (s != null) {
			// 设置text的content
			setText(s.toString());
		}
		// 设置text的color,如果没有设置,默认值为0xFF000000
		setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));
		// 设置text的文字大小
		int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
		if (textSize > 0) {
			setTextSize(textSize);
		}
		// 之前设置的属性,设置循环利用
		a.recycle();
	}

	/**
	 * 新建画笔和设置画笔的样式
	 */
	private final void initLabelView() {
		mTextPaint = new Paint();
		mTextPaint.setAntiAlias(true);
		mTextPaint.setTextSize(16);
		mTextPaint.setColor(0xFF000000);
		setPadding(3, 3, 3, 3);
	}


	/**
	 * 设置文字内容,可以类外使用new view后,再使用view.setText设置内容,在构造器处被调用
	 * @param text
	 */
	public void setText(String text) {
		mText = text;
		requestLayout();
		invalidate();
	}

	
	/**设置字体大小,在构造器处被调用
	 * @param size
	 */
	public void setTextSize(int size) {
		mTextPaint.setTextSize(size);
		requestLayout();
		invalidate();
	}

	
	/**设置字体颜色,在构造器处被调用
	 * @param color
	 */
	public void setTextColor(int color) {
		mTextPaint.setColor(color);
		invalidate();
	}

	
	/**设置自定义view的显示的大小
	 * @see android.view.View#onMeasure(int, int)
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//measureWidth和measureHeight事自定义的两个用来设置宽度和高度的方法
		setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
	}

	/**
	 * 设置宽度,写法一般都遵循这样的框架,需要getMode和getSize来分辨用户设置的“match_parent”还是“wrap_content”等
	 * @param 具体的值         
	 * @return 返回宽度
	 */
	private int measureWidth(int measureSpec) {
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);

		if (specMode == MeasureSpec.EXACTLY) {
			// 确切地知道大小,即设置width=“123”等这些具体数值
			result = specSize;
		} else {
			// 这里相当于设置宽度为wrapContent
			result = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight();
			if (specMode == MeasureSpec.AT_MOST) {
				// 相当于设置成UNSPECIFIED
				result = Math.min(result, specSize);
			}
		}

		return result;
	}

	/**
	 *与measureWidth方法类似
	 */
	private int measureHeight(int measureSpec) {
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);

		mAscent = (int) mTextPaint.ascent();
		if (specMode == MeasureSpec.EXACTLY) {
			result = specSize;
		} else {
			result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom();
			if (specMode == MeasureSpec.AT_MOST) {
				result = Math.min(result, specSize);
			}
		}
		return result;
	}

	/**
	 * 描述view的画法的一个方法,在invalidate时回调
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
	}
}

其中context.obtainStyledAttributes(attrs, R.styleable.LabelView);指的是从xml中获取属性集合
你可以新建一项位于values下的xml文件     resource下面包含以下代码

 <declare-styleable name="LabelView">
        <attr format="string" name="text" />
        <attr format="color" name="textColor" />
        <attr format="dimension" name="textSize" />
    </declare-styleable>

或者像这样写

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <declare-styleable name="EditTextExt">
        <attr name="Text" format="reference|string"></attr>
        <attr name="Oriental">
            <enum name="Horizontal" value="1"></enum>
            <enum name="Vertical" value="0"></enum>
        </attr>
    </declare-styleable>
</resources>

上面里面有一个enum枚举两个选项,供用户只能在这两个选项中选一个,否则编译报错

分析measureWidth和measureHeight方法
依据specMode的值,(MeasureSpec有3种模式分别是UNSPECIFIED, EXACTLY和AT_MOST)

如果是AT_MOST,specSize 代表的是最大可获得的空间; 

如果是EXACTLY,specSize 代表的是精确的尺寸; 
如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。

那么这些模式和我们平时设置的layout参数fill_parent, wrap_content有什么关系呢?
经过代码测试就知道,当我们设置width或height为fill_parent时,容器在布局时调用子 view的measure方法传入的模式是EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。
而当设置为 wrap_content时,容器传进去的是AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。当子view的大小设置为精确值时,容器传入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式目前还没有发现在什么情况下使用。 
   View的onMeasure方法默认行为是当模式为UNSPECIFIED时,设置尺寸为mMinWidth(通常为0)或者背景drawable的最小尺寸,当模式为EXACTLY或者AT_MOST时,尺寸设置为传入的MeasureSpec的大小。 
   有个观念需要纠正的是,fill_parent应该是子view会占据剩下容器的空间,而不会覆盖前面已布局好的其他view空间,当然后面布局子 view就没有空间给分配了,所以fill_parent属性对布局顺序很重要。以前所想的是把所有容器的空间都占满了,难怪google在2.2版本里把fill_parent的名字改为match_parent.

  在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

最后,这个view就可以像平时我们在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/org.yuchen.customview"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <org.yuchen.customview.LabelView
        android:background="@drawable/blue"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:text="Blue"
        app:textSize="20dp" />
    <org.yuchen.customview.LabelView
        android:background="@drawable/red"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:text="Red" />
</LinearLayout>

注意需要有命名空间,所以你明白为什么我们每个xml布局都包含xmlns:android="http://schemas.android.com/apk/res/android"这句话了吧,因为用到android:layout_width等的引用

xmlns:app="http://schemas.android.com/apk/res/org.yuchen.customview"

 必须包含完整包名路径
<org.yuchen.customview.LabelView

下面两个是我们在xml中自定义的两个属性 

app:text="Blue"
app:textSize="20dp"

自定义view还可以有第二种写法

package com.terry.attrs;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

public class EditTextExt1 extends LinearLayout {

    private String Text = "";

    public EditTextExt1(Context context) {
        this(context, null);
        // TODO Auto-generated constructor stub
    }

    public EditTextExt1(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        int resouceId = -1;

        TextView tv = new TextView(context); 
        EditText et = new EditText(context);

        resouceId = attrs.getAttributeResourceValue(null, "Text", 0);
        if (resouceId > 0) {
            Text = context.getResources().getText(resouceId).toString();
        } else {
            Text = "";
        }
        tv.setText(Text);

        addView(tv);
        addView(et, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
                LayoutParams.WRAP_CONTENT));
        this.setGravity(LinearLayout.VERTICAL);

    }

}

这种写法,简单明了,不需要额外XML的配置,就可以在我们的VIEW文件下使用。

以上代码通过构造函数中引入的AttributeSet 去查找XML布局的属性名称,然后找到它对应引用的资源ID去找值。使用也时分方便。所以一直以来我也是很喜欢这种写法。

如上,自定好VIEW文件就可以在XML布局下如此使用:

<com.terry.attrs.EditTextExt1 android:id="@+id/ss3"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        Text="@string/app_name" ></com.terry.attrs.EditTextExt1>

部分代码参考自http://www.cnblogs.com/TerryBlog/archive/2010/11/03/1868431.html

抱歉!评论已关闭.