参考链接:
http://www.iteye.com/topic/602737
这个写的很不错,我是跟着一步步写下来的,不过到最后也遇到了麻烦,就是不能将Tab标签的文字和图片分开,始终是重合的,而且每个具体的代码,还是搞了许久才出来,故而分享之,希望能给大家带来方便,也谢谢下面的高人,呵呵!
http://www.youmi.net/bbs/thread-102-1-4.html
这个和上面的代码是一样的,不过代码不全,对于初学者来说,考验的时候来了,完善就是提高的过程,不要怕麻烦!
下面就根据上面的参考自己写的,当然大部分是相同的,不过有详细的注释,完整的代码
如果有什么不明白就留言吧!呵呵
首先结果图:
图1:
图2:
图3:
当然界面没有前面的仁兄好看,我是讲究实用,美化需要自己慢慢做了
呵呵
下面直接代码:
package com.woclub.tabactivitytest; import android.app.TabActivity; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TabHost; import android.widget.TabWidget; import android.widget.TextView; import android.widget.TabHost.OnTabChangeListener; /** * 总结:在设置Tab的布局的时候首先需要newTabSpec再在其设置setIndicator(Tab名字,Tab的图标), * 尤其需要注意setContent(),它有三种使用方法setContent(int)它是直接在布局文件中设置其布局, * setContent(Intent)可以用Intent指定一个Activity, * setContent(TabContentFactory)可以用一个类来指定其布局的方式 * @author Administrator * */ public class MainActivity extends TabActivity { private static final String Tab1 = "Tab1"; private static final String Tab2 = "Tab2"; private static final String Tab3 = "Tab3"; private static final String Tab4 = "Tab4"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //1得到TabHost对象,正对TabActivity的操作通常都有这个对象完成 final TabHost tabHost = this.getTabHost(); final TabWidget tabWidget = tabHost.getTabWidget(); //2生成一个Intent对象,该对象指向一个Activity,当然现在例子比较简单就没有绑定其他的Activity故而不用 //3生成一个TabSpec对象,这个对象代表了一个Tab页 TabHost.TabSpec tabSpec = tabHost.newTabSpec(Tab1); //设置该页的indicator(指示器)设置该Tab页的名字和图标,以及布局 tabSpec.setIndicator(composeLayout("爽哉", R.drawable.coke)) .setContent(R.id.view1); //4将设置好的TabSpec对象添加到TabHost当中 tabHost.addTab(tabSpec); //第二个Tab tabHost.addTab(tabHost.newTabSpec(Tab2).setIndicator(composeLayout("安逸", R.drawable.coke)) .setContent(R.id.view2)); //第三个Tab tabHost.addTab(tabHost.newTabSpec(Tab3).setIndicator(composeLayout("开心", R.drawable.coke)) .setContent(R.id.view3)); //第四个Tab tabHost.addTab(tabHost.newTabSpec(Tab4).setIndicator(composeLayout("说明", R.drawable.coke)) .setContent(R.id.view4)); //setContent(TabContentFactory)可以用一个类来指定其布局的方式,前三个都是用的setContent(int)方式 // CustomLayout custom = new CustomLayout(this); // tabHost.addTab(tabHost.newTabSpec(Tab4).setIndicator("Tab4", getResources() // .getDrawable(R.drawable.icon)) // .setContent(custom)); //*****************************这是对Tab标签本身的设置******************************************* int width =45; int height =48; for(int i = 0; i < tabWidget.getChildCount(); i++) { //设置高度、宽度,不过宽度由于设置为fill_parent,在此对它没效果 tabWidget.getChildAt(i).getLayoutParams().height = height; tabWidget.getChildAt(i).getLayoutParams().width = width; /** * 下面是设置Tab的背景,可以是颜色,背景图片等 */ View v = tabWidget.getChildAt(i); if (tabHost.getCurrentTab() == i) { v.setBackgroundColor(Color.GREEN); //在这里最好自己设置一个图片作为背景更好 //v.setBackgroundDrawable(getResources().getDrawable(R.drawable.chat)); } else { v.setBackgroundColor(Color.GRAY); } } //************************************************************************************ //设置Tab变换时的监听事件 tabHost.setOnTabChangedListener(new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { // TODO Auto-generated method stub //当点击tab选项卡的时候,更改当前的背景 for(int i = 0; i < tabWidget.getChildCount(); i++) { View v = tabWidget.getChildAt(i); if (tabHost.getCurrentTab() == i) { v.setBackgroundColor(Color.GREEN); } else { //这里最后需要和上面的设置保持一致,也可以用图片作为背景最好 v.setBackgroundColor(Color.GRAY); } } } }); } //#################################################################这是设置TabWidget的布局 /** * 这个设置Tab标签本身的布局,需要TextView和ImageView不能重合 * s:是文本显示的内容 * i:是ImageView的图片位置 * 将它设置到setIndicator(composeLayout("开心", R.drawable.coke))中 */ public View composeLayout(String s, int i){ Log.e("Error", "composeLayout"); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); TextView tv = new TextView(this); tv.setGravity(Gravity.CENTER); tv.setSingleLine(true); tv.setText(s); tv.setTextColor(Color.RED); layout.addView(tv, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); ImageView iv = new ImageView(this); iv.setImageResource(i); layout.addView(iv, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); return layout; } //################################################################# }
我都有详细的注释,估计大家都能看懂的,有些地方给了提示,扩展的需要就需要自己去完成了
下面是一个两个布局文件
一个是在layout中设置:
<?xml version="1.0" encoding="utf-8"?> <!-- 一个典型的标签Activity 是由2 部分构成的 且其id都有规定 即: * TabWidget 用于展示标签页 id=tabs * FrameLayout 用于展示隶属于各个标签的具体布局 id=tabconten * TabHost 用于整个Tab布局 id=TabHost 还需注意要将Tab显示在最下面就需要这只LinearLayout时用Bottom --> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:orientation="vertical" android:gravity="bottom" android:layout_width="fill_parent" android:layout_height="fill_parent"> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="200dip" > <RelativeLayout android:id="@+id/view1" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="需要光临第一个Tab"/> <ImageView android:id="@+id/image1" android:layout_height="wrap_content" android:layout_below="@id/text1" android:layout_width="wrap_content" android:src="/blog_article/@drawable/icon/index.html" /> </RelativeLayout> <TextView android:id="@+id/view2" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="需要光临第二个Tab"/> <TextView android:id="@+id/view3" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="需要光临第三个Tab"/> <TextView android:id="@+id/view4" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout> <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content"> </TabWidget> </LinearLayout> </TabHost>
还有一个在类中设置,设置都差不多,在此类中设置只是针对每个Tab页面的设置
package com.woclub.tabactivitytest; import android.app.Activity; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TabHost; import android.widget.TextView; /** * 此类的功能是设置每个Tab标签的布局方式 * 使用方法 * CustomLayout ct = new CustomLayout(this); * tHost.addTab(tHost.newTabSpec(Tab4).setIndicator("Tab 4").setContent(ct)); * @author Administrator * */ public class CustomLayout implements TabHost.TabContentFactory{ private Activity myActivity; private LayoutInflater layoutHelper;//用于实例化布局 private LinearLayout layout; //构造函数,从外面传递参数Activity public CustomLayout(Activity myActivity) { this.myActivity = myActivity; //通过getLayoutInflater从Activity中得到一个实例化的LayoutInflater layoutHelper = myActivity.getLayoutInflater(); } /** * 根据不同的Tab创建不同的视图 */ @Override public View createTabContent(String tag) { // TODO Auto-generated method stub return addCustomView(tag); } /** * 根据Tab的id设置不同Tab的view * 这是普通的设置方式,设置每个Tab的布局 * @param id * @return */ private View addCustomView(String id) { layout = new LinearLayout(myActivity); layout.setOrientation(LinearLayout.HORIZONTAL); if(id.equals("Tab1")) { ImageView iv = new ImageView(myActivity); iv.setImageResource(R.drawable.chat); //设置layout的布局,将一个ImageView添加到其中,并设置ImageView的布局格式,addView的第二个参数是设置ImageView的width和Height layout.addView(iv, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT)); } else if(id.equals("Tab2")) { //第一个控件,注意每添加一个空间都需要用addView添加到layout中 EditText edit = new EditText(myActivity); layout.addView(edit, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); //第二个控件 Button button = new Button(myActivity); button.setText("确定"); button.setWidth(100); layout.addView(button, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); //第三个控件 RadioGroup rGroup = new RadioGroup(myActivity); rGroup.setOrientation(LinearLayout.HORIZONTAL); RadioButton radio1 = new RadioButton(myActivity); radio1.setText("Radio A"); rGroup.addView(radio1); RadioButton radio2 = new RadioButton(myActivity); radio2.setText("Radio B"); rGroup.addView(radio2); layout.addView(rGroup, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); } else if(id.equals("Tab3")) { TextView text = new TextView(myActivity); text.setText("the third TextView"); text.setGravity(Gravity.CENTER); layout.addView(text); } else if(id.equals("Tab4")) { LinearLayout.LayoutParams param3 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT); //在这里面又引用了布局文件来设置控件 layout.addView(layoutHelper.inflate(R.layout.hello, null),param3); } return layout; } }
好了,该说的都在代码中说明了
希望大家喜欢,做的粗糙,就由大家去改进吧!
呵呵!
欢迎大家的讨论
Activity小品
一、程序效果
1.程序窗口中有一个按钮Button和TextView,我们可以通过写过代码修改该Button和TextView上便签
二、代码编写和解析
1.在HelloWorld程序(Android版HelloWorld附件)基础上
2.编写layout包下的main.xml文件,主要是添加Button控件和为控件指定id
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <!-- android:id 为控件指定id 使用:@+id/控件名 android:layout_width 指定控件的宽 android:layout_height 指定控件的长 fill_parent:填充父控件的宽度,高度,即宽度,高度父控件一样 wrap_content:利用所包裹的内容填充,即内容多大控件多大 --> <TextView android:id="@+id/myTextView" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/myButton" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
3.编写自动生成的Activity类,主要是通过id取得控件并对控件进行一些操作
package linys.activity; import android.app.Activity; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; public class ActivityDemoActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //根据id取得控件 TextView textView=(TextView)findViewById(R.id.myTextView); Button button=(Button)findViewById(R.id.myButton); //对控件上的便签进行修改 textView.setText("textView"); button.setText("button"+"\n"+"text");//wrap_content效果 } }
三、程序解析
1. Activity可以看作是一个控制器,控制着窗体的布局的加载以及窗体上控件的修改,和界面与业务程序间的调用
2.创建Activity类时注意的:
1)继承Activity
2)重写OnCreate方法
3)在AndroidMainfest.xml中配置
(一 ) 查询数据库没有关闭游标
描述:
程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor 后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。
示例代码:
Cursor cursor = getContentResolver().query(uri ...);
if (cursor.moveToNext()) {
... ...
}
修正示例代码:
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri ...);
if (cursor != null && cursor.moveToNext()) {
... ...
}
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
//ignore this
}
}
}
(二 ) 构造 Adapter 时,没有使用缓存的 convertView
描述:
以构造ListView 的 BaseAdapter 为例,在 BaseAdapter 中提高了方法:
public View getView(int position, View convertView, ViewGroup parent)
来向ListView 提供每一个 item 所需要的 view 对象。初始时 ListView 会从 BaseAdapter 中根据当前的屏幕布局实例化一定数量的 view 对象,同时 ListView 会将这些 view 对象缓存起来。当向上滚动 ListView 时,原先位于最上面的 list item 的 view 对象会被回收,然后被用来构造新出现的最下面的 list item 。这个构造过程就是由 getView() 方法完成的, getView() 的第二个形参 View convertView 就是被缓存起来的 list item 的 view 对象 ( 初始化时缓存中没有 view 对象则 convertView 是 null) 。
由此可以看出,如果我们不去使用convertView ,而是每次都在 getView() 中重新实例化一个 View 对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。 ListView 回收 list item 的 view 对象的过程可以查看 :
android.widget.AbsListView.java --> void addScrapView(View scrap) 方法。
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
... ...
return view;
}
修正示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
populate(view, getItem(position));
...
} else {
view = new Xxx(...);
...
}
return view;
}
(三 ) Bitmap 对象不在使用时调用 recycle() 释放内存
描述:
有时我们会手工的操作Bitmap 对象,如果一个 Bitmap 对象比较占内存,当它不在被使用的时候,可以调用 Bitmap.recycle() 方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。可以看一下代码中的注释:
/**
* Free up the memory associated with this bitmap's pixels, and mark the
* bitmap as "dead", meaning it will throw an exception if getPixels() or
* setPixels() is called, and will draw nothing. This operation cannot be
* reversed, so it should only be called if you are sure there are no
* further uses for the bitmap. This is an advanced call, and normally need
* not be called, since the normal GC process will free up this memory when
* there are no more references to this bitmap.
*/
(四 ) 释放对象的引用
描述:
这种情况描述起来比较麻烦,举两个例子进行说明。
示例A :
假设有如下操作
public class DemoActivity extends Activity {
... ...
private Handler mHandler = ...
private Object obj;
public void operation() {
obj = initObj();
...
[Mark]
mHandler.post(new Runnable() {
public void run() {
useObj(obj);
}
});
}
}
我们有一个成员变量 obj ,在 operation() 中我们希望能够将处理 obj 实例的操作 post 到某个线程的 MessageQueue 中。在以上的代码中,即便是 mHandler 所在的线程使用完了 obj 所引用的对象,但这个对象仍然不会被垃圾回收掉,因为 DemoActivity.obj 还保有这个对象的引用。所以如果在 DemoActivity 中不再使用这个对象了,可以在 [Mark] 的位置释放对象的引用,而代码可以修改为:
... ...
public void operation() {
obj = initObj();
...
final Object o = obj;
obj = null;
mHandler.post(new Runnable() {
public void run() {
useObj(o);
}
}
}
... ...
示例B:
假设我们希望在锁屏界面(LockScreen) 中,监听系统中的电话服务以获取一些信息 ( 如信号强度等 ) ,则可以在 LockScreen 中定义一个 PhoneStateListener 的对象,同时将它注册到 TelephonyManager 服务中。对于 LockScreen 对象,当需要显示锁屏界面的时候就会创建一个 LockScreen 对象,而当锁屏界面消失的时候 LockScreen 对象就会被释放掉。
但是如果在释放LockScreen 对象的时候忘记取消我们之前注册的 PhoneStateListener 对象,则会导致 LockScreen 无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的 LockScreen 对象没有办法被回收而引起 OutOfMemory, 使得 system_process 进程挂掉。
总之当一个生命周期较短的对象A ,被一个生命周期较长的对象 B 保有其引用的情况下,在 A 的生命周期结束时,要在 B 中清除掉对 A 的引用。
(五 ) 其他
Android应用程序中最典型的需要注意释放资源的情况是在 Activity 的生命周期中,在 onPause() 、 onStop() 、 onDestroy() 方法中需要适当的释放资源的情况。由于此情况很基础,在此不详细说明,具体可以查看官方文档对 Activity 生命周期的介绍,以明确何时应该释放哪些资源
_Android_内存泄漏调试.pdf,以下有下载: