今天学习在编写布局文件的时候遇到Element type "LinearLayout" must be followed by either attribute specifications, ">" or "/>".这个问题,google了一下才发现是自己忘记在<LinearLayout 后面添加>了,确切的说应该是把EditText和Button两个放在了<LinearLayout 里面了,导致的问题出错。而不是在<LinearLayout> </LinearLayout>中间。
上午学习了ListView,感觉比之前的要复杂一些。为了优化效率,提高运行的速度,需要对布局和获取控件进行缓存,达到优化目的。
1.布局缓存。convertView
在getView()方法中还有一个convertView参数,这个参数用于将之前加载好的布局进行缓存
if(convertView==null){
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
else{
view=convertView;
}
在getView()方法中进行判断,当convertView为空,用LayoutInflater加载布局,不为空时,直接对convertView进行重用,提升ListView效率。
2.控件缓存。ViewHolder
ViewHolder需要自己创建一个类,可以对View的findViewById()方法进行优化,还是以上面的为例。
View view;
ViewHolder viewHolder;
if(convertView==null){
view=LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder=new ViewHolder();
viewHolder.fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName=(TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
}else{
view=convertView;
viewHolder=(com.example.listviewtest.ViewHolder) view.getTag();
}
class ViewHolder {
TextView fruitName;
ImageView fruitImage;
}
当convertView为空的时候,创建一个ViewHolder对象,并把控件实例都存放到ViewHolder里面,然后调用view的setTag()方法,将ViewHolder对象存储在View当中,当convertView不为空时调用view的getTag()方法,把viewHolder重新取出,这样所有的控件实例都缓存到了ViewHolder里面,没必要每次都通过findViewById()方法来获取控件实例了。
下午学习了广播,广播分为两种,一种为标准广播,另一种为有序广播,广播是一种可以跨进程的通信方式。
标准广播为广播发出去之后,所有的广播接收器都能收到,即为无法被截断。
有序广播为广播发出去之后,同一时刻只能有一个广播接收器收到,待其处理完之后,才会继续传递,广播接收器有优先级之分,可以截断广播。
1,接收系统广播
1.1,动态注册
新建一个类作为广播接收器,继承Broadcast,重写父类的onReceive()方法,当有广播到来时,onReceive()方法就能得到执行。
public class MainActivity extends Activity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}
protected void onDestory() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager
.getActiveNetworkInfo();
// Toast.makeText(context, "net change", Toast.LENGTH_LONG).show();
if (networkInfo != null && networkInfo.isAvailable()) {
Toast.makeText(context, "network is available",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "network is unavailable",
Toast.LENGTH_SHORT).show();
}
}
}
}
因为网络状态发生改变的时候,系统会发出android.net.conn.CONNECTIVITY_CHANGE,这条广播,即我们的广播想要监听什么广播,就在这里添加对应的action
然后创建了networkChangeReceiver实例,调用registerReceiver()注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传进去,这样NetworkChangeReceiver就会收到所有android.net.conn.CONNECTIVITY_CHANGE的广播了。注意,动态广播接收器一定都要取消注册,在onDestory()方法中调用unregisterReceiver()方法实现。
因为我们要访问系统的关键性信息,为了保证安全性,需要在配置文件中什么权限,在AndroidManifest.xml文件中加入以下权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />"
1.2 静态注册
动态注册很方便,但是必须得等到程序启动之后才能广播,因为注册的逻辑是写在onCreate()方法中的,若要在程序未启动的时候就收到广播,就需要静态注册了。
首先还是新建一个类继承BroadcastReceiver,然后在AndroidManifest.xml文件中加入权限,以及在<application>标签内加入新的<receiver>,所有的静态注册的广播接收器都在这里注册,这样就可以了。通过手机可以观察到此应用需要访问网络状态和开机系统自动启动的权限,这样就很好的完成了静态注册。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name=".BootCompleteReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
2.自定义广播
2.1 发送标准广播
还是首先定义一个类MyBroadcastReceiver来继承BroadcastReceiver,然后在AndroidManifest.xml文件中注册
<receiver android:name=".MyBroadcastReceiver">
<action android:name="com.example.broadcastnormal.MY_BROADCAST" />
</intent-filter>
</receiver>
这样当发送广播时,就发送com.example.broadcastnormal.MY_BROADCAST的广播。
可以在在布局文件中添加一个按钮,作为发送广播的触发点,在MainActivity中修改
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
"com.example.broadcastnormal.MY_BROADCAST");
sendOrderedBroadcast(intent, null);
}
});
首先构建一个Intent对象,把要发送的广播的值传入,调用Context的sendBroadcast()方法将广播发出去,监听com.example.broadcastnormal.MY_BROADCAST的广播接收机就能收到消息。
2.2发送有序广播
前面的都一样,在MainActivity中的onCreate()方法中的onClick中把sendBroadcast()方法改成sendOrderBroadcast()方法,接收两个参数,一个为Intent,另一个为与权限相关的字符串,这里填入null即可。
sendOrderedBroadcast(intent, null);
改变广播的先后顺序,在注册的时候设定,修改AndroidManifest.xml文件,通过设置优先级
<receiver android:name=".MyBroadcastReceiver">
<intent-filter android:priority="100">
<action android:name="com.example.broadcastnormal.MY_BROADCAST" />
</intent-filter>
</receiver>
优先级高的先收到,获得广播的优先权后,可以选择允许是否向下继续传递,在MyBroadcastReceiver中,加入abortBroadcast()即可。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Received in MyBroadcastReceiver", Toast.LENGTH_LONG).show();
abortBroadcast();
}
}