===================================================================
Android 在Java代碼中設置style屬性--使用代碼建立ProgressBar對象
在andriod開發中,很大一部分都要與資源打交道,好比說:圖片,佈局文件,字符串,樣式等等。這給咱們想要開發一些公共的組件帶來很大的困難,由於公共的組件可能更願意以jar包的形式出現。可是java的jar包中只容許出現java代碼而不能出現資源。
當咱們想要以jar包的形式提供咱們本身開發的公共組件時,咱們就須要把以代碼的形式建立資源。
下面提供一個使用全Java代碼的形式建立一個ProgressBar。
ProgressBar默認的樣式是一個圈圈,咱們要想其顯示爲進度條的樣式能夠在佈局文件中使用以下代碼:
<ProgressBar android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal" />
上面的關鍵代碼是紅色的部分,這部分的代碼就是使得ProgressBar由轉圈圈的樣式變成進度條的樣式。使用這種方式建立的ProgressBar不能包含在jar包中。
一樣咱們也可使用純代碼的形式建立ProgressBar對象,以下:
...
ProgressBar progressBar = new ProgressBar(context);
LineanerLayout layout = new LinearLayout(context);
layout.addView(progressBar, new LayoutParam(LayoutParam.FILL_PARENT, LayoutParam.FILL_PARENT));
....
這樣就使用純代碼的方式建立了一個ProgressBar對象,可是他還只是默認的樣式一個不停的轉的圈圈。
這時咱們可能都會想到好像沒有設置樣式。咱們能夠把以前的那個樣式設進去,可是咱們找遍API發現View並無提供任何給咱們設置樣式的方法。
其實樣式就是經過一種方式給一個View或一組View設置一些共同的屬性值,因此不可能能使用代碼來設置。
咱們能夠看下progressBarStyleHorizontal樣式中給View設置了哪些屬性,咱們找到framework下的res目錄下的values/Theme.xml文件,搜索progressBarStyleHorizontal會發現以下行:
<item name="progressBarStyleHorizontal">@android :style/Widget.ProgressBar.Horizontal</item>
該主題對應的Widget樣式是Widget.ProgressBar.Horizontal,咱們在一樣的的目錄下打開style.xml文件,搜索該樣式,能夠找到以下代碼:
<style name="Widget.ProgressBar.Horizontal">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">@android :drawable/progress_horizontal</item>
<item name="android:indeterminateDrawable">@android :drawable/progress_indeterminate_horizontal</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
</style>
也就是progressBarStyleHorizontal樣式實際上就是設置瞭如上的屬性,咱們直接在佈局文件中把如上的值設進去,代碼看起來以下:
<ProgressBar android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="false"
android:progressDrawable="@android :drawable/progress_horizontal"
android:indeterminateDrawable="@android :drawable/progress_indeterminate_horizontal"
android:minHeight="20dip"
android:maxHeight="20dip" />
這時運行咱們的程序,發現ProgressBar已從圈圈變成進度條的樣式。這時咱們能夠在代碼中把這些屬性設成佈局文件中的值,純Java代碼看起來應該以下面的那樣:
ProgressBar progressBar = new ProgressBar(this);
progressBar.setIndeterminate(false);
progressBar.setProgressDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));
progressBar.setIndeterminateDrawable(getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal));
progressBar.setMinimumHeight(20);
LinearLayout layout = new LinearLayout(this);
layout.addView(progressBar, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setContentView(layout);
這時咱們發現ProgressBar確實變成了橫條,但並無顯示成進度條的樣子,咱們仔細對比一下純Java代碼和xml佈局文件之間差別,咱們發現android:indeterminateOnly="false"和 progressBar.setIndeterminate(false);並不徹底同樣佈局文件的屬性有一個Only結尾但代碼中並無,咱們查找Api發現並無setIndeterminateOnly這樣的一個方法。
咱們打開ProgressBar的源代碼,找到.setIndeterminate(false) 方法,方法的代碼以下:
public synchronized void setIndeterminate(boolean indeterminate) {
if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
mIndeterminate = indeterminate;
if (indeterminate) {
// swap between indeterminate and regular backgrounds
mCurrentDrawable = mIndeterminateDrawable;
startAnimation();
} else {
mCurrentDrawable = mProgressDrawable;
stopAnimation();
}
}
}
咱們這時候能夠發現Indeterminate和IndeterminateOnly並非同一個東西,這時咱們應該想的到,只要咱們把IndeterminateOnly的值變成false就可使ProgressBar變成進度條的樣式,咱們查找全部的代碼,發現並無提供相應的公開方法來修改該屬性的值。
也就是說,咱們討論了那麼久發現根本就沒法經過純代碼的形式來建立一個進度條樣式的ProgressBar.
可是。。。
不就是改變一個類的私有變量的值嘛,Java的封裝性其實並無我想的那麼好,咱們徹底能夠經過反射機制來修改一個對象的私有變量的值,因爲該文章並非討論反射的的文章,因此這裏只給出經過反射來修改私有變量值的代碼,但並不做詳細的說明:
咱們建立一個新的類,叫BeanUtils.java
類得內容看其來以下:
public class BeanUtils {
private BeanUtils() {
}
/**
* 直接設置對象屬性值,無視private/protected修飾符,不通過setter函數.
*/
public static void setFieldValue(final Object object, final String fieldName, final Object value) {
Field field = getDeclaredField(object, fieldName);
if (field == null)
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
makeAccessible(field);
try {
field.set(object, value);
} catch (IllegalAccessException e) {
Log.e("zbkc", "", e);
}
}
/**
* 循環向上轉型,獲取對象的DeclaredField.
*/
protected static Field getDeclaredField(final Object object, final String fieldName) {
return getDeclaredField(object.getClass(), fieldName);
}
/**
* 循環向上轉型,獲取類的DeclaredField.
*/
@SuppressWarnings("unchecked")
protected static Field getDeclaredField(final Class clazz, final String fieldName) {
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// Field不在當前類定義,繼續向上轉型
}
}
return null;
}
/**
* 強制轉換fileld可訪問.
*/
protected static void makeAccessible(Field field) {
if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
field.setAccessible(true);
}
}
}
該工具提供一個共有的方法:public static void setFieldValue(final Object object, final String fieldName, final Object value)來修改一個對象的私有變量的值:
這時咱們的ProgressBar代碼看起來應該以下:
ProgressBar progressBar = new ProgressBar(this);
BeanUtils.setFieldValue(progressBar, "mIndeterminateOnly", new Boolean(false));
progressBar.setIndeterminate(false);
progressBar.setProgressDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));
progressBar.setIndeterminateDrawable(getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal));
progressBar.setMinimumHeight(20);
LinearLayout layout = new LinearLayout(this);
layout.addView(progressBar, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
setContentView(layout);
到此爲止咱們終於使用純java代碼的方式建立了一個ProgressBar的進度條樣式。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
android中ProgressBar的使用
範例說明
Android的Widget,有許可能是爲了與User交互而特別設計的,但也有部分是做爲程序提示、顯示程序運行狀態的Widget。如今介紹的範例,與前一章介紹過的ProgressDialog對話框的應用目的類似,但因爲前章介紹的ProgressDialog是繼承自Android.app.ProgressDialog所設計的互動對話窗口,在應用時,必須新建ProgressDialog對象,在運行時會彈出「對話框」做爲提醒,此時應用程序後臺失去焦點,直到進程結束後,纔會將控制權交給應用程序,若是在Activity當中不但願後臺失焦,又但願提示User有某後臺程序正處於忙碌階段,此時,ProgressBar就會派上用場了。
Android提供的ProgressBar Widget控件與ProgressDialog應用目標不一樣,在程序一開始便可在main.xml Layout當中佈局,先將部署在Layout裏的ProgressBar的屬性設爲隱藏(一開始看不見),然後使用進程來「僞裝」程序忙碌中,但不一樣的是,可在進程當中取得運行時的進度,在「運行」的過程當中,將運行進度經過TextView顯示出來。本範例除了學習ProgressBar Widget的顯示及使用以外,另外一個學習關鍵則是Handler的使用,由於新起的進程沒法訪問Activity裏的Widget,也沒法將運行狀態外送出來,因此須要經過Handler及Message對象,將進程裏的狀態往外傳遞,最後由Activity的Handler事件接收取得運行的狀態。
範例程序
src/irdc.ex04_17/EX04_17.java
爲了讓Thread運行過程當中,能夠不斷地將信息往Activity傳遞,因此用了Android.os.Handler對象及Android.os.Message對象,且在類成員變量中聲明瞭兩個整數:GUI_STOP_NOTIFIER與GUI_THREADING_NOTIFIER,這兩個整數將做爲信息傳遞出來時的信號標識,前者爲當Thread須要喊停的時候處理,後者爲進程正在運行過程當中所需處理的標識。
程序中設計了一個按鈕,此按鈕的工做是讓本來部署在main.xml裏的ProgressBar顯示出來(原來是設置爲Android:visibility="gone"),而由於默認在main.xml中沒有指定它的indeterm- inate屬性,因此即使在程序中強制調用了ProgressBar的setIndeterminate()方法,也沒法改變ProgressBar.getProgress()的值,這個值將永遠爲0。所以,筆者想要使用循環圖片動畫做爲運行過程當中的動畫,並用了一個Counter(整數)來遞增,表示運行的百分比。
public class EX04_17 extends Activity
{
private TextView mTextView01;
private Button mButton01;
private ProgressBar mProgressBar01;
public int intCounter=0;
protected static final int GUI_STOP_NOTIFIER = 0x108;
protected static final int GUI_THREADING_NOTIFIER = 0x109;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton01 = (Button)findViewById(R.id.myButton1);
mTextView01 = (TextView)findViewById(R.id.myTextView1);
mProgressBar01 = (ProgressBar)findViewById(R.id.myProgressBar1);
mProgressBar01.setIndeterminate(false);
mButton01.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
mTextView01.setText(R.string.str_progress_start);
mProgressBar01.setVisibility(View.VISIBLE);
mProgressBar01.setMax(100);
mProgressBar01.setProgress(0);
new Thread(new Runnable()
{
public void run()
{
for (int i=0;i<10;i++)
{
try
{
intCounter = (i+1)*20;
Thread.sleep(1000);
if(i==4)
{
Message m = new Message();
m.what = EX04_17.GUI_STOP_NOTIFIER;
EX04_17.this.myMessageHandler.sendMessage(m);
break;
}
else
{
Message m = new Message();
m.what = EX04_17.GUI_THREADING_NOTIFIER;
EX04_17.this.myMessageHandler.sendMessage(m);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}).start();
}
});
}
Handler myMessageHandler = new Handler()
{
// @Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case EX04_17.GUI_STOP_NOTIFIER:
mTextView01.setText(R.string.str_progress_done);
mProgressBar01.setVisibility(View.GONE);
Thread.currentThread().interrupt();
break;
case EX04_17.GUI_THREADING_NOTIFIER:
if(!Thread.currentThread().isInterrupted())
{
mProgressBar01.setProgress(intCounter);
mTextView01.setText
(
getResources().getText(R.string.str_progress_start)+
"("+Integer.toString(intCounter)+"%)\n"+
"Progress:"+
Integer.toString(mProgressBar01.getProgress())+
"\n"+"Indeterminate:"+
Boolean.toString(mProgressBar01.isIndeterminate())
);
}
break;
}
super.handleMessage(msg);
}
};
}
擴展學習
範例程序中,調用mProgressBar01.setIndeterminate(false),不顯示背景進度Bar,若設置爲mProgressBar01.setIndeterminate(true),也沒法讓默認的ProgressBar圖片(轉圈圈)有正確的進度提示,理由是默認的ProgressBar不支持indeterminate mode循環圖片方式,因此即使setIn- determinate(true)也沒法正確顯示進度。在本程序中,爲刻意寫出做爲對照練習,通常在未知「進度」的狀況下,可改用文字的方式顯示進度百分比,經過調用mProgressBar01.getProgress()取得運行進度值,顯示在文字中。請將Layout裏的ProgressBar Widget定義中,加上一個android: indeterminateOnly屬性,指定其值爲false,不顯示後臺進度Bar,以下所示:
<ProgressBar
android:id="@+id/myProgressBar1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:orientation="horizontal"
android:progressBarStyle=
"@android:style/Widget.ProgressBar.Horizontal"
android:indeterminateOnly="false"
android:visibility="gone"
/>
ProgressBar除了上述關於Android:progressBarStyle的屬性設置以外,筆者也調查了在線Android的源代碼(http://source.android.com),一些本來Android所使用的progressBarStyleHori- zontal屬性,除默認「圓形」的圖片以外,還有其餘的主題及方形圖片Drawable模式可使用。
<resources>
<declare-styleable name="Theme">
<!-- snip -->
<attr name="progressBarStyleHorizontal" format="reference" />
</resources>
接下來看看,這段主題中的屬性名稱progressBarStyleHorizontal定義在frameworks/base/ core/res/res/values/ styles.xml裏,以下所示:
<resources>
<style name="Widget.ProgressBar.Horizontal">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">
@android:drawable/progress_horizontal
</item>
<item name="android:indeterminateDrawable">
@android:drawable/progress_indeterminate_horizontal
</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
</style>
</resources>
因而可知,若是想讓Android使用其餘樣式的ProgressBar,能夠在本來的Layout(main.xml)裏添加如下兩項屬性,以觀察運行過程當中的圖片變化。
android:progressDrawable="@android:drawable/progress_horizontal"
android:indeterminateDrawable=
"@android:drawable/progress_indeterminate_horizontal"java