Android之setContentView和LayoutInflater
1)setContentView(int layoutResID)
2)setContentView(View view)
3)setContentView(View view, ViewGroup.LayoutParams params)
2.用法
1)setContentView(R.layout.main); 2)LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = (View) inflater.inflate(R.layout.apploader, null, true); setContentView(view);
3.两种用法的适用场景:
setContentView()一旦调用, layout就会立刻显示UI;而inflate只会把Layout形成一个以view类实现成的对象,有需要时再用setContentView(view)显示出来。 一般在activity中通过setContentView()将界面显示出来,但是如果要在非activity中如何对控件布局进行设置操作,就需LayoutInflater动态加载。
LayoutInflater:
在实际工作中,事先写好的布局文件往往不能满足我们的需求,有时会根据情况在代码中自定义控件,这就需要用到LayoutInflater。LayoutInflater在Android中是“扩展”的意思,作用类似于findViewById(),不同的是LayoutInflater是用来获得布局文件对象的,而findViewById()是用来获得具体控件的。LayoutInflater经常在BaseAdapter的getView方法中用到,用来获取整个View并返回。
获得 LayoutInflater 实例的三种方式
1. LayoutInflater inflater = getLayoutInflater(); //调用Activity的getLayoutInflater() 2. LayoutInflater localinflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 3. LayoutInflater inflater = LayoutInflater.from(context);
其实,这三种方式本质是相同的,从源码中可以得出结论:这三种方式最终本质是都是调用的Context.getSystemService()。
注意:getSystemService是Activity中的方法,根据传入的name来取得对应的服务对象,这些服务名称参数都是Context类中的常量:
传入的Name 返回的对象 说明
WINDOW_SERVICE WindowManager 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态
POWER_SERVICE PowerManger 电源的服务
ALARM_SERVICE AlarmManager 闹钟的服务
NOTIFICATION_SERVICE NotificationManager 状态栏的服务
KEYGUARD_SERVICE KeyguardManager 键盘锁的服务
LOCATION_SERVICE LocationManager 位置的服务,如GPS
SEARCH_SERVICE SearchManager 搜索的服务
VEBRATOR_SERVICE Vebrator 手机震动的服务
CONNECTIVITY_SERVICE Connectivity 网络连接的服务
WIFI_SERVICE WifiManager Wi-Fi服务
TELEPHONY_SERVICE TeleponyManager 电话服务
横竖屏切换时Activity的生命周期
1、新建一个Activity,并把各个生命周期打印出来
2、运行Activity,得到如下信息
onCreate-->
onStart-->
onResume-->
3、按crtl+f12切换成横屏时
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
5、修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",执行步骤3
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onConfigurationChanged-->
7、把步骤5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",执行步骤3,就只打印onConfigChanged
onConfigurationChanged-->
8、执行步骤4
onConfigurationChanged-->
onConfigurationChanged-->
总结:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
总结一下整个Activity的生命周期
补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop onRestart -->onStart--->onResume
Activity未被完全覆盖只是失去焦点:onPause--->onResume
刷Android界面刷新
ndroid的invalidate与postInvalidate都是用来刷新界面的,用法区别在于:
1)invalidate():实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
// 在onCreate()中开启线程 new Thread(new GameThread()).start(); // 实例化一个handler Handler myHandler = new Handler() { // 接收到消息后处理 public void handleMessage(Message msg) { switch (msg.what) { case Activity01.REFRESH: mGameView.invalidate(); // 刷新界面 break; } super.handleMessage(msg); } }; class GameThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = Activity01.REFRESH; // 发送消息 Activity01.this.myHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
2)使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可
class GameThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 使用postInvalidate可以直接在线程中更新界面 mGameView.postInvalidate(); } } }
重写ScrollView实现两个ScrollView的同步滚动显示
1.背景介绍
最近项目用到两个ScrollView的同步显示,即拖动左边的ScrollView滚动的同时,实现右边的ScrollView同步滚动。此种情形常用在复杂界面布局中,比如左边的ScrollView显示主要项目,只需上下滚动即可;右边项目是次要项目,可以实现上下或者左右滚动,当上下滚动时,需要左右两边的同步显示。
如图所示,左侧是主项目(日期和股票代码),右侧是次要项目(开盘价、最高价、成交量....等等信息)。因为信息比较多,左侧的主项目需要上下拖动显示,而右侧则需要上下左右都可以拖动才能显示完全(ScrollView嵌套一个HorizontalScrollView)。我们希望左侧或右侧上下拖动时,能够实现同步。这就需要实现两个ScrollView的同步显示。因为Android控件中没有此种功能,因此需要重写ScrollView。
2.思路介绍
我们首先想到使用ScrollView的类似与setOnScrollChangedListener的方法来实现,当一个ScrollView滚动时,触发该方法进而使另外一个ScrollView滚动。不过很遗憾,谷歌没有提供该方法。通过查询相应的源代码,我们发现该方法的原型
protected void onScrollChanged(int x, int y, int oldx, int oldy)
该方法是protected类型,不能直接调用,于是需要重新实现ScrollView。
3.具体实现
首先,定一个一个接口(ScrollViewListener.java):
public interface ScrollViewListener { void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy); }
我们需要重写ScrollView才能实现该借口,因此有下面的代码(ObservableScrollView.java):
package com.devin; import android.content.Context; import android.util.AttributeSet; import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private ScrollViewListener scrollViewListener = null; public ObservableScrollView(Context context) { super(context); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public void setScrollViewListener(ScrollViewListener scrollViewListener) { this.scrollViewListener = scrollViewListener; } @Override protected void onScrollChanged(int x, int y, int oldx, int oldy) { super.onScrollChanged(x, y, oldx, oldy); if(scrollViewListener != null) { scrollViewListener.onScrollChanged(this, x, y, oldx, oldy); } } }
接下来是界面的XML,这里是一个简单的Demo,如下(main.xml):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#ffffff" android:orientation="horizontal" > <com.devin.ObservableScrollView android:id="@+id/scrollview1" android:layout_width="400dp" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="monday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="tuesday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="wednesday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="thursday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="friday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="saturday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="sunday" android:textColor="#000000" /> </LinearLayout> </com.devin.ObservableScrollView> <com.devin.ObservableScrollView android:id="@+id/scrollview2" android:layout_width="400dp" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="monday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="tuesday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="wednesday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="thursday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="friday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="saturday" android:textColor="#000000" /> <TextView android:layout_width="wrap_content" android:layout_height="200dp" android:layout_weight="1" android:text="sunday" android:textColor="#000000" /> </LinearLayout> </com.devin.ObservableScrollView> </LinearLayout>
最后是我们的主程调用(PadTestActivity.java):
package com.devin; import android.app.Activity; import android.os.Bundle; public class PadTestActivity extends Activity implements ScrollViewListener { private ObservableScrollView scrollView1 = null; private ObservableScrollView scrollView2 = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1); scrollView1.setScrollViewListener(this); scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2); scrollView2.setScrollViewListener(this); } public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) { if(scrollView == scrollView1) { scrollView2.scrollTo(x, y); } else if(scrollView == scrollView2) { scrollView1.scrollTo(x, y); } } }
代码一目了然,具体就不讲解了,关键是思路,即如何才能想到是怎么实现的
Android之ExpandableList扩展用法(基于BaseExpandableListAdapter)
1.简介
基于基于BaseExpandableListAdapter扩展的ExpandableList用法,现在网上流行的主要有两种:第一种是向BaseExpandableListAdapter传入两个数组,第一个是表示Group(目录头)信息的一维数组,第二个是表示Child(目录子项)的二维数组数组;第二种是构建两个类,一个是表示目录信息的GroupInfo类,另一个是表示子项信息的ChildInfo类,然后传入BaseExpandableListAdapter。通过对比发现,第一种方法由于数组是固定的,而实际项目中往往需要动态变化的目录和子项,因此用处不大,第二种方法文件太多,实现复杂。这里提供一种方法,传递两个个动态的二维数组来实现目录结构。
2.案例
package com.devin; import java.util.ArrayList; import android.app.Activity; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class PadTestActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayList<String> groupList = new ArrayList<String>(); for (int i = 0; i < 3; i++) { groupList.add("title"); } ArrayList<String> itemList1 = new ArrayList<String>(); itemList1.add("Item1"); itemList1.add("Item2"); ArrayList<String> itemList2 = new ArrayList<String>(); itemList2.add("Item1"); itemList2.add("Item21"); itemList2.add("Item3"); ArrayList<String> itemList3 = new ArrayList<String>(); itemList3.add("Item1"); itemList3.add("Item2"); itemList3.add("Item3"); itemList3.add("Item4"); ArrayList<ArrayList<String>> childList = new ArrayList<ArrayList<String>>(); childList.add(itemList1); childList.add(itemList2); childList.add(itemList3); ExpandableListView list = new ExpandableListView(this); ExpandableListAdapter mAdapter = new MyExpandableListAdapter(groupList, childList); list.setAdapter(mAdapter); list.setCacheColorHint(0x00000000); list.setSelector(new ColorDrawable(Color.TRANSPARENT)); list.setGroupIndicator(null); for (int i = 0; i < mAdapter.getGroupCount(); i++) { list.expandGroup(i); } setContentView(list); } private class MyExpandableListAdapter extends BaseExpandableListAdapter { private ArrayList<String> groupList; private ArrayList<ArrayList<String>> childList; MyExpandableListAdapter(ArrayList<String> groupList, ArrayList<ArrayList<String>> childList) { this.groupList = groupList; this.childList = childList; } public Object getChild(int groupPosition, int childPosition) { return childList.get(groupPosition).get(childPosition); } private int selectedGroupPosition = -1; private int selectedChildPosition = -1; public void setSelectedPosition(int selectedGroupPosition, int selectedChildPosition) { this.selectedGroupPosition = selectedGroupPosition; this.selectedChildPosition = selectedChildPosition; } public long getChildId(int groupPosition, int childPosition) { return childPosition; } public int getChildrenCount(int groupPosition) { return childList.get(groupPosition).size(); } public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { TextView textView = null; if (convertView == null) { textView = new TextView(PadTestActivity.this); textView.setPadding(32, 10, 0, 10); convertView = textView; } else { textView = (TextView) convertView; } textView.setText(getChild(groupPosition, childPosition).toString()); if (groupPosition == selectedGroupPosition) { if (childPosition == selectedChildPosition) { textView.setBackgroundColor(0xffb6ddee); } else { textView.setBackgroundColor(Color.TRANSPARENT); } } textView.setOnClickListener(new OnClickListener() { public void onClick(View v) { setSelectedPosition(groupPosition, childPosition); notifyDataSetChanged(); } }); return textView; } public Object getGroup(int groupPosition) { return groupList.get(groupPosition); } public int getGroupCount() { return groupList.size(); } public long getGroupId(int groupPosition) { return groupPosition; } public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { LinearLayout cotain = new LinearLayout(PadTestActivity.this); cotain.setPadding(0, 10, 0, 10); cotain.setGravity(Gravity.CENTER_VERTICAL); ImageView imgIndicator = new ImageView(PadTestActivity.this); TextView textView = new TextView(PadTestActivity.this); textView.setText(getGroup(groupPosition).toString()); textView.setPadding(5, 0, 0, 0); if (isExpanded) { imgIndicator.setBackgroundResource(R.drawable.macro_minus); } else { imgIndicator.setBackgroundResource(R.drawable.macro_plus); } cotain.addView(imgIndicator); cotain.addView(textView); return cotain; } public boolean hasStableIds() { return true; } public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } } }
上述代码中,过向BaseExpandableListAdapter传递两个动态数组groupList(表示目录头信息)和childList (表示子项信息)来构建目录,一方面能够实现动态的添加数据,另一方面简化了实现,一举两得。另外,重写的BaseExpandableListAdapter,如果应用在实际项目中,需要对getGroupView()和getChildView()方法进行构建缓存(和ListView构建一样),以便优化性能和防止内存泄漏。需要的朋友可以自己构建。