功能要求是屏幕上固定显示 3 个 Layout 项(图片+文字),支持点击切换到选择的 Layout 项,并支持滑动切换到最近的 Layout 项。
最后的效果如下:
下面逐步上代码:
布局文件 activity_main.xml 如下:
xmlns:tools="http://schemas.android.com/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)如下:
public class HSVLayout extends LinearLayout {
private HSVAdapter adapter;
private Context context;
public HSVLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
/**
* 设置布局适配器
*
* @param layoutWidthPerAvatar 指定了每一个 item 的占用宽度
* @param adapter 适配器
* @param notify 在点击某一个 item 后的回调
*/
public void setAdapter(int layoutWidthPerAvatar, HSVAdapter adapter,
final INotifySelectItem notify) {
this.adapter = adapter;
for (int i = 0; i
final Map map = adapter.getItem(i);
View view = adapter.getView(i, null, null);
// 为视图设定点击监听器
final int finalI = i;
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 点击选择了某一个 Item 视图
notify.select(finalI);
}
});
this.setOrientation(HORIZONTAL);
// 设置固定显示的每个 item 布局的宽度
this.addView(view, new LinearLayout.LayoutParams(
layoutWidthPerAvatar, LayoutParams.WRAP_CONTENT));
}
}
}
HSVLayout 中的每一个 视图 item 都是由 HSVAdapter 进行设置的,这个比较简单,只控制了每一个 item 的展示,不影响整个水平滚动视图:
public class HSVAdapter extends BaseAdapter {
private static final String TAG = "HSV";
private List> lstAvatars;
private Context context;
private int layoutWidthPerAvatar;
public HSVAdapter(Context context, int layoutWidthPerAvatar){
this.context=context;
this.lstAvatars =new ArrayList>();
this.layoutWidthPerAvatar = layoutWidthPerAvatar;
}
@Override
public int getCount() {
return lstAvatars.size();
}
@Override
public Map getItem(int location) {
return lstAvatars.get(location);
}
@Override
public long getItemId(int arg0) {
return arg0;
}
public void addObject(Map map){
lstAvatars.add(map);
notifyDataSetChanged();
}
@Override
public View getView(int location, View arg1, ViewGroup arg2) {
View view = LayoutInflater.from(context).inflate(R.layout.user_avatar,null);
view.setLayoutParams(new ViewGroup.LayoutParams(layoutWidthPerAvatar,
ViewGroup.LayoutParams.WRAP_CONTENT));
TextView tvIndex = (TextView) view.findViewById(R.id.index_tv);
tvIndex.setText("index-" + String.valueOf(location));
return view;
}
}
其对应的布局文件 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:
public class MainActivity extends ActionBarActivity
implements INotifySelectItem {
private static final String TAG = "Main";
private HorizontalScrollView hsv;
private HSVLayout layoutAvatar;
private HSVAdapter adapterAvatar;
private TextView tvScrollX;
private int layoutWidthPerAvatar = 0;
private Integer[] 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
};
// 记录当前居中的头像索引
private int currentIndex = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int width = DisplayUtil.getScreenWidth(this);
int layoutWidth = (int) (width - getResources().getDimension(R.dimen.activity_horizontal_margin) * 2);
// 每一个头像占用的宽度
layoutWidthPerAvatar = layoutWidth / 3;
hsv = (HorizontalScrollView) findViewById(R.id.hsv);
hsv.setOnTouchListener(new View.OnTouchListener() {
private int lastScrollX = 0;
private int TouchEventId = -9987832;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == TouchEventId) {
if (lastScrollX == hsv.getScrollX()) {
// 停止滚动,计算合适的位置(采用四舍五入)
int indexScrollTo = Math.round(lastScrollX/(layoutWidthPerAvatar*1.0f));
Log.d(TAG, "stop scroll - " + 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
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "touch event - action: " + event.getAction()
+ "|" + event.getX()
+ "|" + event.getY()
+ "|" + hsv.getScrollX()
+ "|" + hsv.getScrollY());
if (event.getAction() == MotionEvent.ACTION_UP) {
handler.sendMessageDelayed(handler.obtainMessage(TouchEventId), 100);
}
return false;
}
});
layoutAvatar = (HSVLayout) findViewById(R.id.avatar_layout);
adapterAvatar = new HSVAdapter(this, layoutWidthPerAvatar);
for (int i = 0; i
Map map = new HashMap();
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
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void select(int position) {
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;
}
}
}
public void onClickScrollX(View view) {
tvScrollX.setText("Scroll.x = " + String.valueOf(hsv.getScrollX()));
}
}