创新源于模仿之二:美化ListView的尝试

简介:
 

今天继续,模仿MIUI做那个Contacts的ListView,如下图:

 

 

 

其实可以这样归纳一下我们要做的事情:


1. 按首字母分组,显示一个分组的标签头。

2. 在右边做一个全字母表,可以用手指上下滑动快速选择字母分组。

3. 再做一个当前选中的那个字母的显示。


先说第一件事。我们已经了解ListView/ListAdapter组合做出一个列表界面。那么,怎么在列表中显示一些不可选且模样不同的行,在SDK提供的例子其实是有相关代码可供参考的。

相关的代码是在ListAdapter中这两个方法:

 

@Override
public boolean areAllItemsEnabled() {
   return false; //不是所有项都可选
}
@Override
public boolean isEnabled(int position) {
   return !getItem(position).name.startsWith("@section"); //如果名字以@section开头,则该项不可选
}


 

简言之,在position这个项是否可选完全可以由你来控制的。

模样的问题也容易解决,只是这样做的效率会降低:

public View getView(int position, View convertView, ViewGroup parent) {
// 如果每一项都一样,就可以这样。
//	 if(convertView==null){
//	 convertView=mInflater.inflate(R.layout.friends_list_row, null);
//	 }

//但我们每一项的模样都可能不一样,只能这样了
FriendInfo item = (FriendInfo)getItem(position);
if(item!=null){
if(!item.name.startsWith("@section")){
convertView=mInflater.inflate(R.layout.friends_list_row, null);
//... ...
convertView.setTag(item);
}
else {
convertView=mInflater.inflate(R.layout.friends_list_section, null);
// ... ...
}
}
return convertView;
}


 

可以运行看看,第一个问题解决了。

第二个问题的处理就是找一张图,放在ListView的右边即可。先看看我们的Layout文件片断:

 

注意这个QuickAlphabeticBar是我们自定义的一个View,extends ImageButton,所以你可以先试试用ImageButton放这儿也能很快看到效果。

 

<FrameLayout
        	android:layout_width="fill_parent" 
        	android:layout_height="0.0dip" 
        	android:layout_weight="1.0">
       
       <ListView 
android:id="@id/friends_list"
android:scrollbars="vertical"
android:layout_width="fill_parent" 
android:layout_height="fill_parent"
android:layout_marginLeft="0.0dip" 
android:drawSelectorOnTop="false"
android:scrollingCache="true" 
android:layout_weight="1.0"
android:fastScrollEnabled="false" 
android:footerDividersEnabled="true"
android:cacheColorHint="#00000000"
style="@style/Widget.ListViewGreen"
/>
 
<cn.sharetop.xmessenger.ui.QuickAlphabeticBar
android:layout_gravity="top|right|center" 
android:id="@id/fast_scroller" 
android:background="@null" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_marginTop="10.0px" 
android:scaleType="centerInside"
android:src="@drawable/contact_list_scroll_long" />
 
  <TextView
  android:id="@id/fast_position"
   	 android:textSize="48dip"
   	 android:textColor="#99FFFFFF"
   	 android:background="@drawable/fast_scroller_overlay"
   	 android:layout_gravity="center_horizontal|top"
   	 android:padding="2dip"
   	 android:layout_margin="24dip"
   	 android:layout_width="@dimen/header_width"
   	 android:layout_height="@dimen/header_width"
   	 android:gravity="center"/>
    
</FrameLayout>


 

很好,这个字母表有了,因为我们需要对手指在上面滑动时的事件进行处理,所以我们自定义它,无非就是要处理这个onTouchEvent(MotionEvent event) 事件罢了,怎么处理?大家应该都想到了,就是根据手指所在位置算一下是哪个字母。

 

所以呢,我们先处理一下那个ListAdapter,让它implements SectionIndexer。

 

public class FriendsListAdapter extends ArrayAdapter<FriendInfo> implements SectionIndexer{
private Context mContext;
private LayoutInflater mInflater;
private HashMap<String, Integer> alphaIndexer; 
private String[] sections = new String[0]; 
public FriendsListAdapter(Context context, int textViewResourceId,
List<BuddyInfo> objects) {
super(context, textViewResourceId, objects);
mContext=context;
mInflater=LayoutInflater.from(mContext);
initSections(objects);
}
//......
@Override
public int getPositionForSection(int section)   { 
          String letter = sections[section]; 
          return alphaIndexer.get(letter); 
    } 
 
@Override
public int getSectionForPosition(int position)  { 
        int prevIndex = 0; 
        for(int i = 0; i < sections.length; i++)         { 
            if(getPositionForSection(i) > position && prevIndex <= position) { 
                prevIndex = i; 
                break; 
            } 
            prevIndex = i; 
        } 
        return prevIndex; 
    } 
@Override
public Object[] getSections() {
return sections; 
}
private void initSections(List<BuddyInfo> items){
alphaIndexer = new HashMap<String, Integer>(); 
        for(int i = items.size() - 1; i >= 0; i--) { 
        	BuddyInfo element = items.get(i); 
            String firstChar = element.sortKey;//.substring(0, 1).toUpperCase(); 
            if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') 
                firstChar = "#"; 
            alphaIndexer.put(firstChar, i); 
        } 
        Set<String> keys = alphaIndexer.keySet(); 
        Iterator<String> it = keys.iterator(); 
        ArrayList<String> keyList = new ArrayList<String>(); 
        while(it.hasNext()) 
            keyList.add(it.next()); 
        Collections.sort(keyList); 
        sections = new String[keyList.size()]; 
        keyList.toArray(sections); 
}
}

 

 

然后,在QuickAlphabeticBar里的onTouchEvent里,我们可以快速定位到相应的段上:

 

@Override
public boolean onTouchEvent(MotionEvent event) {
int act=event.getAction();
//......
float y = event.getY();
//算手指位置,找到对应的段,让mList移动段开头的位置上
if(mListAdapter!=null){
int x=getAlphabeticPostion(y);
if(x<0)x=0;
if(x>=mSections.length) x=mSections.length-1;
int pos=((SectionIndexer)mListAdapter).getPositionForSection(x);
this.mList.setSelectionFromTop(pos, 0);
}
else{
this.mList.setSelectionFromTop(getAlphabeticPostion(y),0);
}
return super.onTouchEvent(event);
}


 

 

OK了,现在我们已经解决两个问题了,但是如何显示一个小的浮动窗口提示当前选中的段的首字母呢,也不复杂,大家注意到文章开头那个Layout中的ID为fast_position的TextView了吧?就是它了。

 

public class QuickAlphabeticBar extends ImageButton implements OnScrollListener {
//... ...
public void init(Context ctx){
mDialogText=(TextView)((FriendsActivity)ctx).findViewById(R.id.fast_position);
mDialogText.setVisibility(View.INVISIBLE);
       
mReady = true;
//... ...       
}
//... ...
@Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
if (mReady) {
        
        	if(mListAdapter!=null && mListAdapter.getCount()>0){
        	 BuddyInfo item = (BuddyInfo)mListAdapter.getItem(firstVisibleItem);
       
           char firstLetter = (item.sortKey!=null && item.sortKey.trim().length()>0)?item.sortKey.toUpperCase().charAt(0):' ';
           
        
           if (!mShowing && firstLetter != mPrevLetter) {
               mShowing = true;
               mDialogText.setVisibility(View.VISIBLE);
              
           }
           mDialogText.setText(((Character)firstLetter).toString());
           mHandler.removeCallbacks(mRemoveWindow);
           mHandler.postDelayed(mRemoveWindow, 1000);
           mPrevLetter = firstLetter;
        	}
        }
}
//... ...
}


 

这段代码就是ListView滚动时,让mDialogText显示出来,并且设置它的内容为首字母即可。关键是那个mHandler,即1秒后要记得让它隐藏起来。

 

 

就是这么多事情了,大家可以试试。

相关文章
|
2月前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
1月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
47 11
|
2月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
94 3
|
2月前
|
Swift iOS开发 UED
【绝妙创意】颠覆你的视觉体验!揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【8月更文挑战第13天】本文通过一个具体案例,介绍如何使用Swift与UIKit在iOS应用中创建独特的按钮动画效果。当按钮被按下时,其形状从圆形变化为椭圆形,颜色则从蓝色渐变为绿色;释放后,动画反向恢复原状。利用UIView动画方法及弹簧动画效果,实现了平滑自然的过渡。通过调整参数,开发者可以进一步优化动画体验,增强应用的互动性和视觉吸引力。
45 7
|
2月前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
87 0
|
2月前
|
C# 机器学习/深度学习 搜索推荐
WPF与机器学习的完美邂逅:手把手教你打造一个具有智能推荐功能的现代桌面应用——从理论到实践的全方位指南,让你的应用瞬间变得高大上且智能无比
【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)应用中集成机器学习功能,以开发具备智能化特性的桌面应用。通过使用Microsoft的ML.NET框架,本文演示了从安装NuGet包、准备数据集、训练推荐系统模型到最终将模型集成到WPF应用中的全过程。具体示例代码展示了如何基于用户行为数据训练模型,并实现实时推荐功能。这为WPF开发者提供了宝贵的实践指导。
35 0
|
5月前
|
算法 程序员 开发者
代码与禅意:技术修炼中的悟性之旅
【5月更文挑战第30天】 在编程世界的林间小径上,每一位开发者都是一位探索者。本文将带你走进程序员的内心世界,透过技术的表象,探讨那些看似无形却能显著提升开发效率和代码质量的“软技能”。从心法到手法,从个人的静心冥想到团队间的默契配合,我们将一探究竟,如何在技术的海洋中找到自己的航向,以及如何让每一行代码都充满“禅意”。
|
5月前
|
机器学习/深度学习 人工智能 vr&ar
从概念到现实:ChatGPT 和 Midjourney 的设计之旅
从概念到现实:ChatGPT 和 Midjourney 的设计之旅
170 0
微信小游戏开发系列教程2-了解游戏全貌和一些游戏开发中的术语
这一节小蚂蚁将会带着大家先从整体上了解一个小游戏的全貌,然后再熟悉一些游戏开发领域中常用的术语。最后分享一下自己的一些经验和方法,希望能够帮助到那些刚进入游戏开发领域的新人。 欢迎体验我的微信小游戏作品:精致1010
93 0
|
Web App开发 前端开发 开发者
新时代布局中一些有意思的特性
新时代布局中一些有意思的特性
226 0
新时代布局中一些有意思的特性