参考链接:
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,以下有下载: