功能要求是屏幕上固定显示 3 个 Layout 项(图片+文字),支持点击切换到选择的 Layout 项,并支持滑动切换到最近的 Layout 项。
最后的效果如下:
下面逐步上代码:
布局文件 activity_main.xml 如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 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"> <TextView android:text="" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <HorizontalScrollView android:id="@+id/hsv" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbarStyle="outsideInset"> <cn.steven.hsvimageswitch.HSVLayout android:id="@+id/avatar_layout" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </HorizontalScrollView> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/hsv" android:layout_marginTop="12dp" android:id="@+id/scrollx_tv"/> <Button 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="滚动位置"/> </RelativeLayout>
上面的 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 < adapter.getCount(); i++) { final Map<String, Object> 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<Map<String,Object>> lstAvatars; private Context context; private int layoutWidthPerAvatar; public HSVAdapter(Context context, int layoutWidthPerAvatar){ this.context=context; this.lstAvatars =new ArrayList<Map<String,Object>>(); this.layoutWidthPerAvatar = layoutWidthPerAvatar; } @Override public int getCount() { return lstAvatars.size(); } @Override public Map<String,Object> getItem(int location) { return lstAvatars.get(location); } @Override public long getItemId(int arg0) { return arg0; } public void addObject(Map<String,Object> 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 如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <ImageView android:layout_width="64dp" android:layout_height="64dp" android:src="@drawable/avatar"/> <TextView android:id="@+id/index_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" /> </LinearLayout>
最后看一下主页面 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[] images = { 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 < images.length; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("image", images[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())); } }
时间: 2024-10-12 02:38:29