动手学Android之十二——补充点控件知识

有些事情,去实现了以后,会产生一些效果,这些效果是你意料之外的

         这节我们来学习一些新的控件,我们了解的控件越多,我的手上的筹码就越多,做起应用来就越游刃有余,这节我们还会复习下listView和asyncTask以及style这几节的一些知识,学知识就是要滚动性前进。

         首先我们来做一个listView出来,相信这对你来说已经不是问题,我就直接上效果图啦:


         这里面做了一些之前style的练习,首先,我们的应用程序没有标题栏,并且全屏了,这是因为我设置了Theme

<style name="ControlListTheme" parent="android:Theme.NoTitleBar.Fullscreen">
</style>

         其他的也就是给listView设置了一个selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:state_pressed="true">
        <shape>
            <solid android:color="@color/list_press" />
        </shape>
    </item>

</selector>

         这个selector没有用drawable,而是用的shape,这样显然用的空间比图片小些,而且shape的定义也很灵活,稍后我们会看到,这里用solid指定了颜色。

         其他的都是之前见到过的,就不重复了。

         但是这样做后会有一个问题哦,我们按住我们的item下滑,就会出现一个黑屏。


这是在我们改变listView的背景色时常见的一个问题,网上有人说是因为列表中的View重绘时,使用了系统默认的黑色,但是我还没有找到依据,这里暂时先不去深入讨论这个问题,有高手知道的请告诉小弟我哈。这里说一下解决方案,android:cacheColorHint=“#00000000”,这个属性就是设置所谓重绘时的背景色的,这里解释一下:之前的颜色都是6位,怎么这里变成8位了呢?8位的话,前两位代表透明度。不要以为这里是将背景设置成透明,这里其实是传递一个0cacheColorHint,这个说明可以在API文档中查到,传递0表示重绘背景是半透明的或者不是单一色。如果不信,你可以试试#00000001,只要稍微改动一下,马上出现黑屏,这才是真正用透明色的效果。不过API比较晦涩,这里我真不是特别懂,真正的高手请不吝赐教啊!

         我们每次点击item就跳转到对应的Activity,我们先来实现ProgressBar,这是一个进度条控件,平时也用得挺多,得好好讲讲:

<ProgressBar
	android:id="@+id/progressBar"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content" 
	/>

         先看下效果:



         这就是一个progressBar,一个不停转的圈,但是我们平时看到的progressBar都是有进度的呀,那是怎么弄出来的呢?

<ProgressBar
	android:id="@+id/progressBar"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	style="?android:attr/progressBarStyleHorizontal"
	/>

         注意这里的style引用格式,引用android系统内的style就是这么写,我们来看下效果先:


         下面我们来实现一个功能,我们点一个按钮,点击后出现一个进度条,并启动一个异步任务,然后每隔500ms让进度条增加10%,直到100%后,隐藏进度条,这里我贴出关于进度条的一些代码,其他代码都是之前讲过的哦

         首先,隐藏进度条android:visibility="gone",这里visibility还有一个invisible,和gone有什么区别呢?gone是指完全消失,就好像没有一样;invisible是只看不见,但是位置还是被占用了。

         然后是异步任务中的代码:

public class ProgressAsyncTask extends AsyncTask<Void, Integer, Void> {

	private ProgressBar progressBar = null;
	private Button button = null;
	
	public ProgressAsyncTask(ProgressBar progressBar, Button button) {
		this.progressBar = progressBar;
		this.button = button;
		this.progressBar.setMax(100);
		this.progressBar.setProgress(0);
		this.progressBar.setVisibility(View.VISIBLE);
	}
	
	@Override
	protected void onPreExecute() {
		// TODO Auto-generated method stub
		super.onPreExecute();
		button.setEnabled(false);
	}
	
	@Override
	protected Void doInBackground(Void... params) {
		// TODO Auto-generated method stub
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(500);
				publishProgress(i + 1);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
		return null;
	}

	@Override
	protected void onProgressUpdate(Integer... values) {
		// TODO Auto-generated method stub
		super.onProgressUpdate(values);
		progressBar.setProgress(values[0].intValue() * 10);
	}
	
	@Override
	protected void onPostExecute(Void result) {
		// TODO Auto-generated method stub
		super.onPostExecute(result);
		progressBar.setVisibility(View.GONE);
		button.setEnabled(true);
	}
	
}

         这里主要是调用setMax和setProgress来设置进度条的全程和进度,用setVisibility来设置View是否可见。相信代码大家都能看懂。


         但是系统自带的进度条往往不能满足我们的需求,我们想自己来定义它的样式,首先我们准备4张图片:


         然后我们在res目录下建立一个anim文件夹,里面建立一个progress_animate.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" 
    android:oneshot="false">
    
    <item android:duration="150" android:drawable="@drawable/progress01" />
    <item android:duration="150" android:drawable="@drawable/progress02" />
    <item android:duration="150" android:drawable="@drawable/progress03" />
    <item android:duration="150" android:drawable="@drawable/progress04" />
    
</animation-list>

         这是一个动画文件,注意这里的图片不能太小,也不要太大,最好和系统圆圈的尺寸差不多,因为这里的wrap_content不会根据我们指定的图片去设置大小,它还是根据系统图片的尺寸来设定大小的。我的例子中使用了200px的图片,但是大小和80px图片一样。

         还有一种方式,我们在drawable中定义一个rotate资源:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromDegrees="0"
    android:toDegrees="360" >
    
    <shape android:shape="ring"
        android:innerRadiusRatio="3"
        android:thicknessRatio="8"
        android:useLevel="false">
        
        <gradient android:type="sweep"
            android:useLevel="false"
            android:startColor="#FFFFFF"
            android:centerColor="#FFDC35"
            android:centerY="0.50"
            android:endColor="#CE0000" />
        
    </shape>

</rotate>

         然后在布局文件中引用:

<ProgressBar
	android:layout_width="wrap_content"
	android:layout_height="wrap_content" 
	android:indeterminate="false"
	android:indeterminateDrawable="@drawable/progress_color"
	android:indeterminateDuration="5000"
	/>

         这样的效果是一个渐变色的环在转,indeterminateDuration可以指定周期,单位ms。


         这里面shape的一些知识这里暂时不讲,毕竟不是这节重点,以后再详讲。

         还有一种方法,可以用layer-list,同样是在drawable中定义:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <item>
        <rotate android:drawable="@drawable/progress01"
            android:fromDegrees="0"
            android:toDegrees="360"
            android:pivotX="50%"
            android:pivotY="50%"
            />
    </item>

</layer-list>

         然后在布局文件中引用,效果也很容易想到。

         同样,我们也希望对水平进度条进行自定义样式,怎么办呢?由于水平进度条是通过指定style来变成水平进度条的,所以我们要自定义一个style,然后继承系统的style:

<style name="my_horizon_progress_style" parent="@android:style/Widget.ProgressBar.Horizontal">
	<item name="android:progressDrawable">@drawable/horizontal_progress</item>
</style>

         而horizontal_progress就是我们自定义的关键:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    
    <item android:id="@android:id/background">
        <shape >
            <corners android:radius="5dp"/>
            <gradient android:startColor="#A286FF"
                android:centerColor="#78DFF6"
                android:endColor="#7CF78B"
                android:centerY="0.75"
                android:angle="270"/>
        </shape>
    </item>
	<item android:id="@android:id/progress">
	    <clip>
	        <shape>
	            <corners android:radius="5dp" />
	            <gradient android:startColor="#F7C67C"
	                android:endColor="#FF3400"
	                android:angle="270" />
	        </shape>
	    </clip>
	</item>
</layer-list>

         这里通过item的id来指定进度条的背景和前景,大家这里能够知道大概意思就好,之后讲了资源,大家再回来看,应该就懂了。我们看下效果:


         Ok,到此为止,ProgressBar介绍得差不多了,其实到这里也仅仅是够用罢了,进度条的知识点还有很多,以后有时间再来深入分析吧。

         下面,我们再来讲一个AutoComplete控件,它是干嘛的呢?这是一个非常好用的控件,实现了自动补全的功能,废话不多说,我们看一下就知道了。

         在我们的布局文件中放一个:

<AutoCompleteTextView
	android:id="@+id/auto_complete"
	android:layout_width="match_parent"
	android:layout_height="wrap_content" 
	/>

         然后我们在Activity中写上

public class AutoCompleteActivity extends Activity {

	private AutoCompleteTextView autoCompleteTextView = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.auto_complete);
		autoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.auto_complete);
		String[] texts = new String[] {"abcd", "abce"};
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, texts);
		autoCompleteTextView.setAdapter(adapter);
	}
	
}

         看看效果:



         这就是AutoComplete,当你输入ab的时候,它就会按照adapter中的String进行匹配,然后给出提示,ArrayAdapter我们已经讲过了,这里不再细讲,下面我们来做这样一件事情,当用户在AutoComplete中输入文字并按确定后,我们把文字加入adapter中,并且我们改变autoComplete的样式。

首先,为了改变样式,我们自定义一个layout,这里我只放一个textView

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textColor="#FF0000"
    android:textSize="30sp"
    android:background="#0000FF" >

</TextView>

         然后,我们在代码中改动:

public class AutoCompleteActivity extends Activity {

	private AutoCompleteTextView autoCompleteTextView = null;
	private Button autoAddBtn = null;
	ArrayAdapter<String> adapter = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.auto_complete);
		autoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.auto_complete);
		autoAddBtn = (Button) findViewById(R.id.auto_add_btn);
		
		adapter = new ArrayAdapter<String>(this, R.layout.auto_complete_dropdown, new String[]{});
		autoCompleteTextView.setAdapter(adapter);
		
		autoAddBtn.setOnClickListener(new OnClickListener() {
			
			public void onClick(View v) {
				// TODO Auto-generated method stub
				String auto_text = autoCompleteTextView.getText().toString();
				if( 0 != auto_text.length() ) {
					adapter.add(auto_text);
				}
			}
		});
		
	}
	
}

         代码我就不解释了,应该都能看懂,想想,这个功能不就是百度搜索框那个历史记录的功能么?如果把添加换成搜索的话。先看下效果吧:



         如果我们想只输入1个字母就让提示框出来,可以设置android:completionThreshold="1"属性,但是如果我们想不输入字母就让提示框出来,就需要自己写类来继承AutoCompleteTextView了。

public class AutoCompleteNoThreshold extends AutoCompleteTextView {
	
	public AutoCompleteNoThreshold(Context context, AttributeSet attributeSet) {
		super(context, attributeSet);
	}
	
	@Override
	public boolean enoughToFilter() {
		// TODO Auto-generated method stub
		return true;
	}
	
	@Override
	protected void onFocusChanged(boolean focused, int direction,
			Rect previouslyFocusedRect) {
		// TODO Auto-generated method stub
		super.onFocusChanged(focused, direction, previouslyFocusedRect);
		performFiltering(getText(), KeyEvent.KEYCODE_UNKNOWN);
	}

}

         这里我们构造函数用了AutoCompleteNoThreshold(Context context, AttributeSet attributeSet),这个函数是当我们使用自定义View的时候系统调用的,我们在xml中写的参数就是从attributeSet进来的(猜的),不明白我在说什么?等下你就明白了。然后我们重载了enoughToFilter函数,这个函数就是决定是否出现提示框的,我们直接返回true,还有,我们在焦点发生变化的时候,也弹出提示框。

         下面我们的布局文件中的AutoCompleteTextView要变一变了:

<com.example.autocomplete.AutoCompleteNoThreshold
	android:id="@+id/auto_complete"
	android:layout_width="0dp"
	android:layout_height="wrap_content"
	android:layout_weight="1" 
	android:completionThreshold="1"
	/>

         用我们自己的类,注意这里要写全路径,哈哈,现在明白刚刚说的事情了吧,如果我们自己定义了一个View,那么我们可以用全路径的方式在布局文件中引进来,关于这个技术我之后还会讲。然后就是代码中使用AutoCompleteNoThreshold去替换AutoCompleteTextView咯,我们看看效果:


         为了看出焦点变化,我们还可以再加一个EditText,然后来回切换焦点。

         注意,这里我们虽然对空字符串作了限制,但是一些重复的字符串有可能加到我们的adapter中来,这时候用ArrayAdapter不太好处理。所以我们可以自己定义Adapter来代替ArrayAdapter,但是这里不能仅仅只定义自己的Adapter去继承BaseAdapter,因为AutoCompleteTextView的adapter要求比BaseAdapter高,还实现了Filterable接口。我们来试一下:

public class AutoCompleteAdapter extends BaseAdapter implements Filterable {

	private Context context = null;
	private Set<String> set = null;
	private MyFilter myFilter = null;
	private List<String> list = null;
	
	public AutoCompleteAdapter(Context context) {
		// TODO Auto-generated constructor stub
		this.context = context;
		set = new HashSet<String>();
		list = new ArrayList<String>();
	}
	
	public void add(String str) {
		set.add(str);
	}
	
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return list.toArray()[position];
	}

	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		TextView textView = null;
		if(convertView == null) {
			textView = new TextView(context);
			textView.setTextSize(20.0f);
			textView.setTextColor(Color.RED);
			textView.setBackgroundColor(Color.BLUE);
		} else {
			textView = (TextView) convertView;
		}
		textView.setText(list.toArray()[position].toString());
		return textView;
	}

	public Filter getFilter() {
		// TODO Auto-generated method stub
		if(myFilter == null) {
			myFilter = new MyFilter();
		}
		return myFilter;
	}
	
	private class MyFilter extends Filter {

		@Override
		protected FilterResults performFiltering(CharSequence constraint) {
			// TODO Auto-generated method stub
			FilterResults filterResults = new FilterResults();
			//在这里自己定义过滤规则
			list.clear();
			Iterator<String> it = set.iterator();
			while(it.hasNext()) {
				String str = it.next();
				if(str.startsWith(constraint.toString())) {
					list.add(str);
				}
			}
			filterResults.count = list.size();
			filterResults.values = list;
			return filterResults;
		}

		@Override
		protected void publishResults(CharSequence constraint,
				FilterResults results) {
			// TODO Auto-generated method stub
			if(results.count > 0) {
				notifyDataSetChanged();
			} else {
				notifyDataSetInvalidated();
			}
		}
		
	}

}

         整个程序稍微有点长,但是思想是利用set不重复的特性,将添加的String加入set中,在过滤器中,我们通过过滤规则将set中符合条件的字符串放到list中,最终展现出来。这里我们继承了Filter这个类,实现了两个方法,performFiltering是一个在工作线程中完成的过滤方法,我们AutoCompleteTextView的输入就通过constraint传递进来,publishResults是在UI线程中的方法,在这里,我们通知数据改变,这样adapter才会去更新自己的数据。这是典型的观察者模式。

         效果大家自己试一下就知道了。

         不知不觉,已经讲了很多了,总结一下:

1、  程序全屏显示

2、  listView拖动变黑问题的解决方案

3、  progressBar基本用法

4、  visibility让视图可见,不可见,消失

5、  progressBar自定义样式,环形的三种方法,水平条的一种方法

6、  AutoCompleteTextView基本用法

7、  自定义AutoCompleteTextView样式

8、  创建自己的View写在xml文件中

9、  自定义Adapter实现Filter规则

知识点较多,希望大家好好消化,这节的例子在:http://download.csdn.net/detail/yeluoxiang/7397327。欢迎大家下载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值