[Android]仿京东手机端类别页

[Android]仿京东手机端类别页

京东手机端的类别标签页, 是一个左侧滑动可选择类别, 右侧一个类别明细的列表联动页面. 当用户选择左侧选项, 可在右侧显示更多选项来选择. 实现方式也不少. 最常见的当然是左侧和右侧各一个Fragment, 左侧Fragment放置ListView, 右侧放显示类别明细的Fragment. 如果觉得页面包含的Fragment太多, 左侧直接给一个ListView就可以了.不影响效果.

效果图:

预览图

例子中值得注意的三点:

  • 左侧列表点击某个Item可以自动上下滑动,使所点击的item自动移至列表中间
  • 点击item后保留背景色不变
  • 右侧布局

针对上面三个点,这里采取如下的解决方法:

  • 计算可见列表的可见首项或末项position值,使用smoothScrollToPosition()方法实现滑动
  • 自定义列表selector按下和松开的背景色,在adapter去更新并控制item的背景色
  • 右侧布局,采用Fragment是最好的. 里面使用ScrollView装载所有数据,可以动态的addView(),removeView(), 网格布局使用GridView. 由于Fragment, 所以更新数据和更新View都非常方便, 所以例子中直接用静态页面模拟数据了.

重在通过简单的例子解释这种实现思路, 当然实现不是唯一的.

然后,我们先来模拟右侧的Fragment数据,一看就懂的代码:


public class JDFragment extends Fragment{
    String TAG = "JDFragment";
    private View rootView = null;
    private LinearLayout llayout_main = null;
    private TextView tv = null;

    private LinearLayout.LayoutParams lp_gd = null;
    private LinearLayout.LayoutParams lp_tv = null;
    private ArrayList<Category> itemList = null;
    private GDAdapter adapter = null;


    @Override
    public void onAttach(Activity activity)
    {
        Log.e(TAG, "onAttach...");
        super.onAttach(activity);
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Log.e(TAG, "onCreate...");
        super.onCreate(savedInstanceState);
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        Log.e(TAG, "onCreateView...");
        rootView = inflater.inflate(R.layout.jd_frg_main, null);
        llayout_main = (LinearLayout) rootView.findViewById(R.id.llayout_jd_frg_main);
        tv = (TextView) rootView.findViewById(R.id.tv_jd_frg_main);

        updateTitle();

        //模拟数据
        for(int i=0; i<2; i++)
        {
            setData();          
        }

        return rootView;
    }

    protected void updateTitle()
    {
        if(getArguments() != null)
        {
            updateTitle(getArguments().getString("name"));
        }
    }

    protected void updateTitle(String title)
    {
        if(tv != null)
        {
            tv.setText(title);
        }
    }

    private void setData()
    {
        if(itemList == null)
        {
            itemList = new ArrayList<Category>();
            for(int i=1; i<11; i++)
            {
                itemList.add(new Category("选项 " + i, ""+i));
            }
        }

        //高度60dp+行距8dp = 68dp
        int heightUnit = (int)TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 68, getResources().getDisplayMetrics());
        int height;

        //计算Gridview总高度
        if(itemList.size() % 3 == 0)
        {
            height = (itemList.size()/3 + 2)*heightUnit;
        }
        else{
            height = (itemList.size()/3 + 1)*heightUnit;
        }

        if(lp_gd == null)
            lp_gd = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height);

        if(lp_tv == null)
            lp_tv = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT
                    , (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
                            , 30, getResources().getDisplayMetrics()));

        TextView tv_title = new TextView(getActivity());
        tv_title.setLayoutParams(lp_tv);
        tv_title.setText("组一");
        llayout_main.addView(tv_title);

        GridView gridView = new GridView(getActivity());
        gridView.setNumColumns(3);
        gridView.setVerticalSpacing(8);
        gridView.setLayoutParams(lp_gd);

        adapter = new GDAdapter(getActivity(), itemList,R.drawable.cate);
        gridView.setAdapter(adapter);
        llayout_main.addView(gridView);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        Log.e(TAG, "onActivityCreated...");
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onStart()
    {
        Log.e(TAG, "onStart...");
        super.onStart();
    }

    @Override
    public void onResume()
    {
        Log.e(TAG, "onResume...");
        super.onResume();
    }

    @Override
    public void onPause()
    {
        Log.e(TAG, "onPause...");
        super.onPause();
    }

    @Override
    public void onStop()
    {
        Log.e(TAG, "onStop...");
        super.onStop();
    }

    @Override
    public void onDestroyView()
    {
        Log.e(TAG, "onDestroyView...");
        super.onDestroyView();
    }

    @Override
    public void onDestroy()
    {
        Log.e(TAG, "onDestroy...");
        super.onDestroy();
    }

    @Override
    public void onDetach()
    {
        Log.e(TAG, "onDetach...");
        super.onDetach();
    }

    static class GDAdapter extends BaseAdapter
    {
        Context context;
        List<Category> results;
        int imageId;
        ViewHolder holder = null;

        public GDAdapter(Context context, List<Category> results,int imageId) {
            this.context = context;
            this.results = results;
            this.imageId = imageId;
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return results.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return results.get(position);
        }


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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Category c = (Category)getItem(position);

            if(convertView == null)
            {
                holder = new ViewHolder();
                convertView = LayoutInflater.from(context).inflate(R.layout.jd_item, null);
                holder.tv = (TextView) convertView.findViewById(R.id.tv_jd_item);
                holder.imv = (ImageView) convertView.findViewById(R.id.imv_jd_item);
            }
            else
            {
                holder = (ViewHolder) convertView.getTag();
            }
            convertView.setTag(holder);

            holder.tv.setText(c.getName());
            holder.imv.setImageResource(imageId);

            return convertView;
        }

        class ViewHolder
        {
            TextView tv;
            ImageView imv;
        }
    }

}

JDFragment的布局文件, jd_frg_main.xml:

<?xml version="1.0" encoding="utf-8" ?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    >
    <TextView 
        android:id="@+id/tv_jd_frg_main"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:text="Fragment"
        android:textSize="16sp"
        android:background="#EEEEEE"
        />
    <ScrollView 
        android:id="@+id/scrlayout_jd_frg_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/tv_jd_frg_main"
        android:overScrollMode="never"
        >
        <LinearLayout 
            android:id="@+id/llayout_jd_frg_main"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            />
    </ScrollView>


</RelativeLayout>

将所有数据放在ScrollView的线性布局中,使用Fragment作为容器, 可以根据需要ADD, Remove和Update数据和View. 到这里右侧页面的简单模拟实现就结束了,都是一目了然的代码.

然后就是实现左侧列表了,先是列表中简单的自定义Adapter, MyAdapter:

public class MyAdapter extends BaseAdapter
{
    private Context context;
    private List<Category> results;
    private int imageId;
    private ViewHolder holder = null;
    private int selectedId;

    public MyAdapter(Context context, List<Category> results,int imageId) {
        this.context = context;
        this.results = results;
        this.imageId = imageId;
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return results.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return results.get(position);
    }


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

    public void setSelected(int position)
    {
        this.selectedId = position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Category c = (Category)getItem(position);

        if(convertView == null)
        {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.jd_item, null);
            holder.tv = (TextView) convertView.findViewById(R.id.tv_jd_item);
            holder.imv = (ImageView) convertView.findViewById(R.id.imv_jd_item);
        }
        else
        {
            holder = (ViewHolder) convertView.getTag();
        }

        if(position == selectedId)
        {
            convertView.setBackgroundResource(R.drawable.sele_true);
        }
        else
        {
            convertView.setBackgroundResource(R.drawable.sele_false);
        }

        holder.tv.setText(c.getName());
        holder.imv.setImageResource(imageId);
        convertView.setTag(holder);

        return convertView;
    }

    class ViewHolder
    {
        TextView tv;
        ImageView imv;
    }
}

然后是主Activity了, 在里面对可见ListView的item位置进行计算, 并进行滑动处理. 当用户点击偏上的item, 列表就往下滑动, 加载顶部更多的item; 当用户点击偏下的item, 列表就往上滑动, 加载底部更多的item.

同时我们自定义按下和松开时的背景文件放在drawable, 随便一个shape就可以了. 然后点击某个item的position时, 在adapter中判断是否目标item, 是就设置按下背景色 - 白色, 否则就是正常的背景色 - 灰色.

/**
 * 仿京东类别页
 * @author AlexTam
 */
public class JDActivity extends FragmentActivity{
    private ListView lv_main = null;
    private EditText et_search = null;

    private ArrayList<Category> itemList = new ArrayList<Category>(); 
    private MyAdapter adapter = null;
    //可见列表项的数量
    private int visibleCount = 0;
    //上次点击的位置
    private int lastPosition = 0;
    private int ce = 0;
    //实际列表是否超出屏幕
    private boolean isOut = true;
    private JDFragment fragment = null;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.jd_main);

        init();
    }


    private void init()
    {
        lv_main = (ListView) findViewById(R.id.lv_main);
        et_search = (EditText) findViewById(R.id.et_search);

        for(int i=1; i<21; i++)
        {
            itemList.add(new Category("选项 " + i, ""+i));
        }

        adapter = new MyAdapter(this, itemList,R.drawable.ic_launcher);
        lv_main.setAdapter(adapter);
        lv_main.setOnItemClickListener(new MyOnItemOnClick());
        lv_main.setSelector(R.color.pink);

        //模拟右侧标签页
        fragment = new JDFragment();
        Bundle bundle = new Bundle();
        bundle.putString("name", "c1");
        fragment.setArguments(bundle);
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.flayout_main, fragment, "c0").commit();

        adapter.setSelected(0);
        adapter.notifyDataSetChanged();
    }

    private class MyOnItemOnClick implements OnItemClickListener
    {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) 
        {
            //计算滑动
            if(visibleCount == 0)
            {
                visibleCount = lv_main.getChildCount();
                if(visibleCount == itemList.size())
                    isOut = false;
                else
                {
                    ce = visibleCount/2;
                }
            }

            if(position <= (parent.getFirstVisiblePosition() + ce))
            {   //上移
                lv_main.smoothScrollToPosition(position - ce);
            }
            else
            {   //下移
                if((parent.getLastVisiblePosition() + ce + 1) <= parent.getCount())
                {
                    lv_main.smoothScrollToPosition(position + ce);
                }
                else
                {
                    lv_main.smoothScrollToPosition(parent.getCount()-1);
                }

            }

            lastPosition = position;

            adapter.setSelected(position);
            adapter.notifyDataSetChanged();

            //更新右侧标签页的标题
            fragment.updateTitle("c" + (position+1));
        }

    }


    /**
     * 选项对象
     */
    static class Category
    {
        private String name;
        private String id;

        Category(String name,String id)
        {
            this.name = name;
            this.id = id;
        }

        public String getName()
        {
            return this.name;
        }
    }



}

OK, 到此效果就出来了. 好简单吧!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值