专栏首页恩蓝脚本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, 到此效果就出来了. 好简单吧!

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • android仿直播圆点加载效果

    今天实现一个很多app中使用到的加载进度条的效果,可能我们平时数据加载都使用到的是系统自带的,但是也有很多app加载进度条的效果实现挺好看,就是三个点不停的水平...

    砸漏
  • Android BannerView通用封装详解

    之前封装过一个,但总觉得不够优雅,就有了这个通用封装,很简洁,不知道够不够优雅,不过原来那个有跟随指示器和丝滑滑动效果,感兴趣可以看一下。

    砸漏
  • Android自定义RecyclerView实现不固定刻度的刻度尺

    本文实例为大家分享了自定义RecyclerView实现不固定刻度的刻度尺的具体代码,供大家参考,具体内容如下

    砸漏
  • android仿直播圆点加载效果

    今天实现一个很多app中使用到的加载进度条的效果,可能我们平时数据加载都使用到的是系统自带的,但是也有很多app加载进度条的效果实现挺好看,就是三个点不停的水平...

    砸漏
  • Java中不可或缺的50个小技巧,好用!

    《Effective JavaJava》名著,必读。如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提升编码素质。

    程序员白楠楠
  • Java接地气日常编码技巧

    Effective Java。Java名著,必读。如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提升编码素质。

    JAVA葵花宝典
  • 读完《Effective Java》: 我整理这 50 条技巧

    以下内容只记录了我自己整理的东西,还是建议读原文。为了聚焦知识点,一些说明故意忽略掉了。相当于是一篇摘要。

    JAVA葵花宝典
  • 读完《Effective Java》后我淦了 50 条开发技巧

    《Effective Java》Java名著,必读。如果能严格遵从本文的原则,以编写API的质量来苛求自己的代码,会大大提升编码素质。

    cxuan
  • 高并发Java(4):无锁

    在高并发Java(1):前言中已经提到了无锁的概念,由于在jdk源码中有大量的无锁应用,所以在这里介绍下无锁。

    用户5640963
  • Spring Boot + Spring Cloud 实现权限管理系统 后端篇(八):MyBatis分页功能实现

    使用Mybatis时,最头痛的就是写分页,需要先写一个查询count的select语句,然后再写一个真正分页查询的语句,当查询条件多了之后,会发现真不想花双倍的...

    朝雨忆轻尘

扫码关注云+社区

领取腾讯云代金券