功能要求是屏幕上固定显示 3 个 Layout 项(图片+文字),支持点击切换到选择的 Layout 项,并支持滑动切换到最近的 Layout 项。
最后的效果如下:
下面逐步上代码:
布局文件 activity_main.xml 如下:
xmlns:tools="/tools"android:layout_width="match_parent"
android:layout_height="match_parent"android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"tools:context=".MainActivity">
android:layout_height="wrap_content"/>
android:id="@+id/hsv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbarStyle="outsideInset">
android:id="@+id/avatar_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/hsv"
android:layout_marginTop="12dp"
android:id="@+id/scrollx_tv"/>
android:onClick="onClickScrollX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/scrollx_tv"
android:layout_marginTop="12dp"
android:text="滚动位置"/>
上面的 HorizontalScrollView 中使用了自定义的 HSVLayout 布局,定义(HSVLayout.java)如下:
publicclassHSVLayoutextendsLinearLayout{
privateHSVAdapteradapter;
privateContextcontext;
publicHSVLayout(Contextcontext,AttributeSetattrs){
super(context,attrs);
this.context=context;
}
/**
*设置布局适配器
*
*@paramlayoutWidthPerAvatar指定了每一个item的占用宽度
*@paramadapter适配器
*@paramnotify在点击某一个item后的回调
*/
publicvoidsetAdapter(intlayoutWidthPerAvatar,HSVAdapteradapter,
finalINotifySelectItemnotify){
this.adapter=adapter;
for(inti=0;i
finalMapmap=adapter.getItem(i);
Viewview=adapter.getView(i,null,null);
//为视图设定点击监听器
finalintfinalI=i;
view.setOnClickListener(newOnClickListener(){
@Override
publicvoidonClick(Viewv){
//点击选择了某一个Item视图
notify.select(finalI);
}
});
this.setOrientation(HORIZONTAL);
//设置固定显示的每个item布局的宽度
this.addView(view,newLinearLayout.LayoutParams(
layoutWidthPerAvatar,LayoutParams.WRAP_CONTENT));
}
}
}
HSVLayout 中的每一个 视图 item 都是由 HSVAdapter 进行设置的,这个比较简单,只控制了每一个 item 的展示,不影响整个水平滚动视图:
publicclassHSVAdapterextendsBaseAdapter{
privatestaticfinalStringTAG="HSV";
privateList>lstAvatars;
privateContextcontext;
privateintlayoutWidthPerAvatar;
publicHSVAdapter(Contextcontext,intlayoutWidthPerAvatar){
this.context=context;
this.lstAvatars=newArrayList>();
this.layoutWidthPerAvatar=layoutWidthPerAvatar;
}
@Override
publicintgetCount(){
returnlstAvatars.size();
}
@Override
publicMapgetItem(intlocation){
returnlstAvatars.get(location);
}
@Override
publiclonggetItemId(intarg0){
returnarg0;
}
publicvoidaddObject(Mapmap){
lstAvatars.add(map);
notifyDataSetChanged();
}
@Override
publicViewgetView(intlocation,Viewarg1,ViewGrouparg2){
Viewview=LayoutInflater.from(context).inflate(R.layout.user_avatar,null);
view.setLayoutParams(newViewGroup.LayoutParams(layoutWidthPerAvatar,
ViewGroup.LayoutParams.WRAP_CONTENT));
TextViewtvIndex=(TextView)view.findViewById(R.id.index_tv);
tvIndex.setText("index-"+String.valueOf(location));
returnview;
}
}
其对应的布局文件 user_avatar.xml 如下:
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/avatar"/>
android:id="@+id/index_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
最后看一下主页面 MainActivity:
publicclassMainActivityextendsActionBarActivity
implementsINotifySelectItem{
privatestaticfinalStringTAG="Main";
privateHorizontalScrollViewhsv;
privateHSVLayoutlayoutAvatar;
privateHSVAdapteradapterAvatar;
privateTextViewtvScrollX;
privateintlayoutWidthPerAvatar=0;
privateInteger[]p_w_picpaths={
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar,
R.drawable.avatar
};
//记录当前居中的头像索引
privateintcurrentIndex=1;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intwidth=DisplayUtil.getScreenWidth(this);
intlayoutWidth=(int)(width-getResources().getDimension(R.dimen.activity_horizontal_margin)*2);
//每一个头像占用的宽度
layoutWidthPerAvatar=layoutWidth/3;
hsv=(HorizontalScrollView)findViewById(R.id.hsv);
hsv.setOnTouchListener(newView.OnTouchListener(){
privateintlastScrollX=0;
privateintTouchEventId=-9987832;
Handlerhandler=newHandler(){
@Override
publicvoidhandleMessage(Messagemsg){
super.handleMessage(msg);
if(msg.what==TouchEventId){
if(lastScrollX==hsv.getScrollX()){
//停止滚动,计算合适的位置(采用四舍五入)
intindexScrollTo=Math.round(lastScrollX/(layoutWidthPerAvatar*1.0f));
Log.d(TAG,"stopscroll-"+lastScrollX
+"|"+layoutWidthPerAvatar
+"|"+lastScrollX/(layoutWidthPerAvatar*1.0f)
+"|"+indexScrollTo);
if(indexScrollTo>0){
hsv.smoothScrollTo(indexScrollTo*layoutWidthPerAvatar,0);
}else{
hsv.smoothScrollTo(0,0);
}
}else{
handler.sendMessageDelayed(
handler.obtainMessage(TouchEventId),100);
lastScrollX=hsv.getScrollX();
}
}
}
};
@Override
publicbooleanonTouch(Viewv,MotionEventevent){
Log.d(TAG,"touchevent-action:"+event.getAction()
+"|"+event.getX()
+"|"+event.getY()
+"|"+hsv.getScrollX()
+"|"+hsv.getScrollY());
if(event.getAction()==MotionEvent.ACTION_UP){
handler.sendMessageDelayed(handler.obtainMessage(TouchEventId),100);
}
returnfalse;
}
});
layoutAvatar=(HSVLayout)findViewById(R.id.avatar_layout);
adapterAvatar=newHSVAdapter(this,layoutWidthPerAvatar);
for(inti=0;i
Mapmap=newHashMap();
map.put("p_w_picpath",p_w_picpaths[i]);
map.put("index",(i+1));
adapterAvatar.addObject(map);
}
layoutAvatar.setAdapter(layoutWidthPerAvatar,adapterAvatar,this);
tvScrollX=(TextView)findViewById(R.id.scrollx_tv);
}
@Override
publicbooleanonCreateOptionsMenu(Menumenu){
//Inflatethemenu;thisaddsitemstotheactionbarifitispresent.
getMenuInflater().inflate(R.menu.menu_main,menu);
returntrue;
}
@Override
publicbooleanonOptionsItemSelected(MenuItemitem){
//Handleactionbaritemclickshere.Theactionbarwill
//automaticallyhandleclicksontheHome/Upbutton,solong
//asyouspecifyaparentactivityinAndroidManifest.xml.
intid=item.getItemId();
//noinspectionSimplifiableIfStatement
if(id==R.id.action_settings){
returntrue;
}
returnsuper.onOptionsItemSelected(item);
}
@Override
publicvoidselect(intposition){
Toast.makeText(this,"select"+String.valueOf(position),
Toast.LENGTH_SHORT).show();
if(position>0){
if(currentIndex!=position){
hsv.smoothScrollTo((position-1)*layoutWidthPerAvatar,0);
currentIndex=position;
}
}
}
publicvoidonClickScrollX(Viewview){
tvScrollX.setText("Scroll.x="+String.valueOf(hsv.getScrollX()));
}
}