天天看点

记录知识点与技巧(一)

去掉ActionBar

android:theme="@android:style/Theme.Holo.NoActionBar"
           
actionBar = getActionBar(); //得到ActionBar
actionBar.hide(); //隐藏ActionBar
           
requestWindowFeature(Window.FEATURE_NO_TITLE);
           

设置全屏

requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
           
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
           

Activity生命周期

记录知识点与技巧(一)

Activity由于在后台状态时,被回收之后如果恢复数据

onSaveInstanceState()//回调方法保证活动被回收之前调用
@Override
protected void onSaveInstanceState(Bundle outState) {
	super.onSaveInstanceState(outState);
	String tempData = "Something you just typed";
	outState.putString("data_key", tempData);
}
           
<span style="font-size:12px;">//展示数据
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	Log.d(TAG, "onCreate");
	requestWindowFeature(Window.FEATURE_NO_TITLE);
	setContentView(R.layout.activity_main);
	if (savedInstanceState != null) {
		String tempData = savedInstanceState.getString("data_key");
		Log.d(TAG, tempData);
	}
	……
}</span>
           

Activity的启动模式

<activity> 标签指定android:launchMode 属性来选择启动模式

standard

singleTop

singleTask

singleInstance

standard
默认模式,启动一个新的活动后,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
singleTop
当活动的启动模式指定为singleTop,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
singleTask
当活动的启动模式指定为singleTask,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这
个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。
singleInstance
指定为singleInstance 模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask 模式指定了不同的taskAffinity,也会启动一个新的返回栈)。
这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
           

假设有三个Activity,跳转打印日志如下

记录知识点与技巧(一)
可以看到, SecondActivity 的Task id 不同于FirstActivity 和ThirdActivity , 这说明SecondActivity 确实是存放在一个单独的返回栈里的,而且这个栈中只有SecondActivity 这一个活动。
然后我们按下Back 键进行返回,你会发现ThirdActivity 竟然直接返回到了FirstActivity,再按下Back 键又会返回到SecondActivity,再按下Back 键才会退出程序,这是为什么呢?
其实原理很简单,由于FirstActivity 和ThirdActivity 是存放在同一个返回栈里的,当在ThirdActivity 的界面按下Back 键,ThirdActivity 会从返回栈中出栈,那么FirstActivity 就成为了栈顶活动显示在界面上,因此也就出现了从ThirdActivity 直接返回到FirstActivity 的情况。然后在FirstActivity 界面再次按下Back 键,这时当前的返回栈已经空了,于是就显示了另一个返回栈的栈顶活动,即SecondActivity。最后再次按下Back 键,这时所有返回栈都已经空了,也就自然退出了程序。
           

知晓当前是在哪一个活动

public class BaseActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
	}
}
           
记录知识点与技巧(一)

随时随地退出程序

public class ActivityCollector {
	public static List<Activity> activities = new ArrayList<Activity>();
	public static void addActivity(Activity activity) {
		activities.add(activity);
	}
	public static void removeActivity(Activity activity) {
		activities.remove(activity);
	}
	public static void finishAll() {
		for (Activity activity : activities) {
			if (!activity.isFinishing()) {
				activity.finish();
			}
		}
	}
}
           

接下来修改BaseActivity 中的代码,如下所示:

public class BaseActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d("BaseActivity", getClass().getSimpleName());
		ActivityCollector.addActivity(this);
	}
	@Override
	protected void onDestroy() {
		super.onDestroy();
		ActivityCollector.removeActivity(this);
	}
}
           

控件的visibility属性

visible 表示控件是可见的,这个值是默认值,不指定android:visibility 时,控件都是可见的。
invisible 表示控件不可见,但是它仍然占据着原来的位置和大小,可以理解成控件变成透明状态了。
gone 则表示控件不仅不可见,而且不再占用任何屏幕空间。
           

相对布局RelativeLayout部分属性含义

android:layout_alignLeft	本元素的左边缘和某元素的的左边缘对齐
android:layout_alignTop	本元素的上边缘和某元素的的上边缘对齐
android:layout_alignRight	本元素的下边缘和某元素的的下边缘对齐
android:layout_alignBottom 本元素的右边缘和某元素的的右边缘对齐
android:layout_alignParentLeft	是否紧靠父元素左边
android:layout_alignParentTop	是否紧靠父元素上方
android:layout_alignParentRight 是否紧靠父元素右边
android:layout_alignParentBottom 是否紧靠父元素的下方
android:layout_centerInParent	在父元素中上下左右居中显示
android:layout_above 在某元素的的上方
android:layout_below 在某元素的下方
android:layout_toLeftOf 在某元素的左边
android:layout_toRightOf 在某元素的右边
           

将字体变粗

//英文中使用android:textStyle=”bold”但是不能将中文加粗
TextView tv = (TextView)findViewById(R.id.TextView01); 
TextPaint tp = tv.getPaint(); 
tp.setFakeBoldText(true);
           

View树

记录知识点与技巧(一)

如何制作.9图

记录知识点与技巧(一)

在Android sdk 目录下有一个tools 文件夹,在这个文件夹中找到draw9patch.bat 文件,我们就是使用它来制作Nine-Patch 图片的。双击打开之后,在导航栏点击File→Open 9-patch将图片加载进来

记录知识点与技巧(一)

我们可以在图片的四个边框绘制一个个的小黑点,在上边框和左边框绘制的部分就表示当图片需要拉伸时就拉伸黑点标记的区域,在下边框和右边框绘制的部分则表示内容会被放置的区域。

动态添加碎片

动态添加碎片主要分为5 步。
1. 创建待添加的碎片实例。
2. 获取到FragmentManager,在活动中可以直接调用getFragmentManager()方法得到。
3. 开启一个事务,通过调用beginTransaction()方法开启。
4. 向容器内加入碎片,一般使用replace()方法实现,需要传入容器的id 和待添加的碎
片实例。
5.提交事务,调用commit()方法来完成。
           

在碎片中模拟返回栈

其实很简单,FragmentTransaction 中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中

AnotherRightFragment fragment = new AnotherRightFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.
beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();
           

在活动中得到相应碎片的实例

RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
//在碎片中得到对应的activity实例
MainActivity activity = (MainActivity) getActivity();
           

碎片完整的生命周期示意图

记录知识点与技巧(一)

碎片还提供了一些附加的回调方法

1. onAttach()
当碎片和活动建立关联的时候调用。
2. onCreateView()
为碎片创建视图(加载布局)时调用。
3. onActivityCreated()
确保与碎片相关联的活动一定已经创建完毕的时候调用。
4. onDestroyView()
当与碎片关联的视图被移除的时候调用。
5. onDetach()
当碎片和活动解除关联的时候调用。
           

如何使用限定符(Qualifiers)

在res目录下新建layout-large 文件夹,在这个文件夹下新建一个布局,也叫做activity_main.xml。layout/activity_main 布局只包含了一个碎片,即单页模式,而layout-large/
activity_main 布局包含了两个碎片,即双页模式。其中large 就是一个限定符,那些屏幕被认为是large 的设备就会自动加载layout-large 文件夹下的布局,而小屏幕的设备则还是会加载layout 文件夹下的布局。
           

双页模式:

记录知识点与技巧(一)

单页模式:

记录知识点与技巧(一)

Android 中一些常见的限定符可以参考下表:

记录知识点与技巧(一)

使用最小宽度限定符

使用large 限定符成功解决了单页双页的判断问题 ,不过很快又有一个新的问题出现了,large 到底是指多大呢?有的时候我们希望可以更加灵活地为不同设备加载布局, 不管它们是不是被系统认定为“ large ”, 这时就可以使用最小宽度限定符(Smallest-width Qualifier)了。

最小宽度限定符允许我们对屏幕的宽度指定一个最小指(以dp 为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局。

在res 目录下新建layout-sw600dp 文件夹,这就意味着,当程序运行在屏幕宽度大于600dp 的设备上时,会加载layout-sw600dp/activity_main 布局,当程序运行在屏幕宽度小于600dp 的设备上时,则仍然加载默认的layout/activity_main 布局。需要注意一点,最小宽度限定符是在Android 3.2 版本引入的;

广播机制(BroadcastReceiver)

为了方便于进行系统级别的消息通知,Android 引入了一套类似的广播消息机制

广播机制简介

为什么说Android 中的广播机制更加灵活呢?这是因为Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的。Android 提供了一套完整的API,

允许应用程序自由地发送和接收广播Android 中的广播主要可以分为两种类型,标准广播和有序广播。

标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

sendBroadcast(intent);
           
记录知识点与技巧(一)

有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

sendOrderedBroadcast(intent, null);
           
记录知识点与技巧(一)

有序广播中如何设定广播接收器的先后顺序呢?

//intent-filter中的priority指代广播接收器的先后顺序
<receiver android:name=".MyBroadcastReceiver">
<intent-filter android:priority="100" >
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
           

android:priority执行 优先级,默认为0,值越大优先级越高,1000是最大值。

使用本地广播

前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何
的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容
易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用
程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播.
           

Android提供LocalBroadcastManager来对本地广播进行管理;

//使用LocalBroadcastManager获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
// 发送本地广播
localBroadcastManager.sendBroadcast(intent); 
// 注册本地广播监听器
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
//选择是否允许广播继续传递
abortBroadcast();
           

Tip:

动态注册的广播接收器一定都要取消注册才行

需要添加查询系统网络状态的权限

android:name="android.permission.ACCESS_NETWORK_STATE"
           

如果要在广播接收器里启动活动的,一定要给Intent 加入FLAG_ACTIVITY_NEW_TASK 这个标志

另外还有一点需要说明,本地广播是无法通过静态注册的方式来接收的

Android数据存储五种方式

1. 使用SharedPreferences存储数据

 2. 文件存储数据      

 3 .SQLite数据库存储数据

 4 .使用ContentProvider存储数据

 5 .网络存储数据

拦截短信

//系统发出的短信广播是一条有序广播,所以可以拦截掉
public class MainActivity extends Activity {
	……
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		……
		receiveFilter = new IntentFilter();
		receiveFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
		receiveFilter.setPriority(100);
		messageReceiver = new MessageReceiver();
		registerReceiver(messageReceiver, receiveFilter);
	}
	……
	class MessageReceiver extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
			……
			abortBroadcast();
		}
	}
}
           

调用摄像头

// 创建File对象,用于存储拍照后的图片
File outputImage = new File(Environment.
getExternalStorageDirectory(), "tempImage.jpg");
try {
	if (outputImage.exists()) {
		outputImage.delete();
	}
outputImage.createNewFile();
} catch (IOException e) {
	e.printStackTrace();
}
imageUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action. IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO); // 启动相机程序
           
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	switch (requestCode) {
		case TAKE_PHOTO:
			if (resultCode == RESULT_OK) {
				Intent intent = new Intent("com.android.camera.action.CROP");
				intent.setDataAndType(imageUri, "image/*");
				intent.putExtra("scale", true);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, CROP_PHOTO); // 启动裁剪程序
			}
			break;
		case CROP_PHOTO:
			if (resultCode == RESULT_OK) {
				try {
					Bitmap bitmap = BitmapFactory.decodeStream
					(getContentResolver()
					.openInputStream(imageUri));
					picture.setImageBitmap(bitmap); // 将裁剪后的照片显示出来
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
			break;
			default:
			break;
	}
}@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	switch (requestCode) {
		case TAKE_PHOTO:
			if (resultCode == RESULT_OK) {
				Intent intent = new Intent("com.android.camera.action.CROP");
				intent.setDataAndType(imageUri, "image/*");
				intent.putExtra("scale", true);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, CROP_PHOTO); // 启动裁剪程序
			}
			break;
		case CROP_PHOTO:
			if (resultCode == RESULT_OK) {
				try {
					Bitmap bitmap = BitmapFactory.decodeStream
					(getContentResolver()
					.openInputStream(imageUri));
					picture.setImageBitmap(bitmap); // 将裁剪后的照片显示出来
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			}
			break;
			default:
			break;
	}
}
           

使用Service还是Thread

(1)默认情况下,Service其实是运行在主线程中的,如果需要执行复杂耗时的操作,必须在Service中再创建一个Thread来执行任务或者使用intentService,大同小异。
(2)Service的优先级高于后台挂起的Activity,当然,也高于Activity所创建的Thread,因此,系统可能在内存不足的时候优先杀死后台的Activity或者Thread,而不会轻易杀死Service组件,即使被迫杀死Service,也会在资源可用时重启被杀死的Service
           

Android 中的定时任务

一种是使用Java API 里提供的Timer 类;

一种是使用Android 的Alarm 机制;

但Timer不具有唤醒CPU 的功能,有可能导致Timer 中的定时任务无法正常运行;

Alarm 机制

AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
           

四种值可选

ELAPSED_REALTIME:让定时任务的触发时间从系统开机开始算起,但不会唤醒CPU。

ELAPSED_REALTIME_WAKEUP:表示让定时任务的触发时间从系统开机开始算起,但会唤醒CPU。

RTC :让定时任务的触发时间从1970 年1月1 日0 点开始算起,但不会唤醒CPU。

RTC_WAKEUP:表示让定时任务的触发时间从1970 年1 月1 日0 点开始算起,但会唤醒CPU。

 AlarmManager的常用API

1、set(int type,long startTime,PendingIntent pi)

该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。

2、setRepeating(int type,long startTime,long intervalTime,PendingIntent pi)

该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第三个参数表示闹钟响应动作。

3、setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi)

该方法也用于设置重复闹钟,与第二个方法相似,不过其两个闹钟执行的间隔时间不是固定的而已。

获取到系统开机至今所经历时间的毫秒数

SystemClock.elapsedRealtime()

获取到1970 年1 月1 日0 点至今所经历时间的毫秒数

System.currentTimeMillis()

PendingIntent的FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT

当使用FLAG_UPDATE_CURRENT时:

PendingIntent.getActivity(context, 0, notificationIntent,PendingIntent.FLAG_CANCEL_CURRENT时);

FLAG_UPDATE_CURRENT会更新之前PendingIntent的消息,比如,你推送了消息1,并在其中的Intent中putExtra了一个值“ABC”,在未点击该消息前,继续推送第二条消息,并在其中的Intent中putExtra了一个值“CBA”,好了,这时候,如果你单击消息1或者消息2,你会发现,他俩个的Intent中读取过来的信息都是“CBA”,就是说,第二个替换了第一个的内容。

当使用FLAG_CANCEL_CURRENT时:

依然是上面的操作步骤,这时候会发现,点击消息1时,没反应,第二条可以点击。

导致上面两个问题的原因就在于第二个参数requestCode,当requestCode值一样时,后面的就会对之前的消息起作用,所以为了避免影响之前的消息,requestCode每次要设置不同的内容。

...

......

............