仿蘑菇街界面应用

蘑菇街作为中国最大女性购物社区,其APP的设计水平也毋庸置疑的,最近博客将连续来仿造一个蘑菇街的APP的界面设计。

(1)准备工作

  在阅读郭霖大神的博客时有人问里面使用的美工素材怎么得到的,其实很简单,下载一个APP,把APK格式修改成rar后解压,你会在目录下看到所有的素材。

        随后,看看APP的界面:

      

仿蘑菇街界面应用仿蘑菇街界面应用

第一个是启动界面,第二个是主界面,先来看第一个界面。

(1)启动界面(Splash)。

启动界面也叫Splash界面,是APP启动时的第一画面,主要用于介绍应用、宣传或者加载数据,或者兼而有之。这里之所以要单独拿出来是因为这个应用有个独特的,它的LOGO是透明渐变出现的,也就是淡入效果,我们知道ANDROID主要有四种动画:透明动画、缩放、位移、旋转,分别使用的AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation,这里我们使用AlphaAnimation。

界面布局很简单,如activity_loading.xml:

public class LoadingActivity extends Activity{  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         //设置全屏  
         this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
         this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);  
         setContentView(R.layout.activity_loading);  
         ImageView view =(ImageView) findViewById(R.id.logo);  
         AlphaAnimation aa = new AlphaAnimation(0.01f,1.0f);//透明度变化  
         aa.setDuration(1500);//设置渐变时间  
         view.startAnimation(aa);//设置渐变的view  
         aa.setAnimationListener(new AnimationListener(){  
             //动画结束后自动执行  
            @Override  
            public void onAnimationEnd(Animation arg0) {  
                redirectTo();  
            }  
            @Override  
            public void onAnimationRepeat(Animation animation) {  
                  
            }  
            @Override  
            public void onAnimationStart(Animation animation) {  
                  
            }  
                                                                            
         });  
    }  
      
    /** 
     * 跳转到登陆界面 
     */  
    private void redirectTo(){         
        Intent intent = new Intent(this, MainActivity.class);  
        startActivity(intent);  
        finish();  
    }  
  
      
}  

注意2个地方:a. AlphaAnimation aa = new AlphaAnimation(0.01f,1.0f); 构造方法:AlphaAnimation(float fromAlpha, float toAlpha),表示从透明度0.01到1.0的渐变,我们知道0.0表示全透明,1.0表示完全不透明.

  b. onAnimationEnd方法,顾名思义,表示:在动画结束后自动执行这个方法,这里当然是跳转到主界面了。运行后其效果如下:

仿蘑菇街界面应用

是不是很简单,以后做启动画面都可以采用类似的方法。

总结要点:AlphaAnimation类

(1)主界面。

看看原图,如下:

仿蘑菇街界面应用

中间的内容先不管,我们看ActionBar和底部菜单,上面是一个自定义的ActionBar,下面是一个切换菜单,而且下面菜单在改变的时候上面的ActionBar也在变。我们这里采用的设计方法是fragment+activity混合使用,底部菜单使用RadioButton,中间留出一个活动的fragment

先来看ActionBar,观察发现ActionBar分为2类,第一个是图片标题,另外一个是文字标题。

所以自定义的actionBar需要2个xml布局文件,分别命名为:actionbar_index.xml和actionbar_usu.xml

其XML布局分别为:actionbar_index.xml

public class ActionBarTool {  
    Activity activity;  
    public ActionBarTool(Activity activity){  
        this.activity=activity;  
    }  
    /** 
     * 设置主界面的Index 
     */  
    public void setIndexActionBar(){  
        ActionBar actionBar=activity.getActionBar();         
        ActionBar.LayoutParams params=new ActionBar.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT,Gravity.CENTER );  
        View view=LayoutInflater.from(activity).inflate(R.layout.actionbar_index, null);  
        actionBar.setCustomView(view,params);  
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);  
        actionBar.setDisplayShowCustomEnabled(true);  
    }  
      
    /** 
     * 设置其他界面的ActionBar 
     * @param title 标题 
     */  
    public void setUsuActionBar(String title){  
        ActionBar actionBar=activity.getActionBar();         
        ActionBar.LayoutParams params=new ActionBar.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT,Gravity.CENTER );  
        View view=LayoutInflater.from(activity).inflate(R.layout.actionbar_usu, null);  
        actionBar.setCustomView(view,params);  
        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);  
        actionBar.setDisplayShowCustomEnabled(true);  
        TextView tv_title=(TextView)activity.findViewById(R.id.tv_title);  
        tv_title.setText(title);  
    }  
}  

这是自定义目录的一般做法,但是安卓的设计文档里面是不推荐这么使用的,因为这样会破坏ActionBar的一些灵活性,而且到后期很难管理,这里之所以使用是因为文字居中需要自己定义视图,其实可以直接用背景来代替,这样也能实现文字居中,而且不会破坏actonbar的灵活性。

再来看看底部菜单:main.XML文件如下

public class MainActivity extends FragmentActivity {  
  
    RadioButton btn_index, btn_category, btn_discovery, btn_cart, btn_my;  
    private Fragment fragment;  
    FragmentManager fragmentManager;  
    ActionBarTool actionbarTool;  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        InitUI();  
    }  
      
    /** 
     * 初始化UI界面 
     */  
    private void InitUI() {  
        actionbarTool=new ActionBarTool(this);  
        btn_index = (RadioButton) findViewById(R.id.fragment_index);  
        btn_category = (RadioButton) findViewById(R.id.fragment_category);  
        btn_discovery = (RadioButton) findViewById(R.id.fragment_discovery);  
        btn_cart = (RadioButton) findViewById(R.id.fragment_cart);  
        btn_my = (RadioButton) findViewById(R.id.fragment_my);  
        btn_my.setOnClickListener(new switchFragment());  
        btn_cart.setOnClickListener(new switchFragment());  
        btn_discovery.setOnClickListener(new switchFragment());  
        btn_category.setOnClickListener(new switchFragment());  
        btn_index.setOnClickListener(new switchFragment());  
        fragment = new IndexFragment();  
        actionbarTool.setIndexActionBar();  
        btn_index.setTextColor(getResources().getColor(R.color.red));  
        btn_index.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.index_indicator_s), null, null);  
        fragmentManager =getSupportFragmentManager();  
        if (fragment != null) {  
            fragmentManager.beginTransaction()  
                    .replace(R.id.content, fragment).commit();  
        } else {  
            Log.e("MainActivity", "Error in creating fragment");  
        }  
    }  
  
    /** 
     * 切换fragment 
     * @author zw.yan 
     *  
     */  
    class switchFragment implements View.OnClickListener {  
        @Override  
        public void onClick(View arg0) {  
            InitBg();//每一次都初始化按钮样式  
            switch (arg0.getId()) {  
            case R.id.fragment_index:  
                fragment = new IndexFragment();  
                actionbarTool.setIndexActionBar();  
                btn_index.setTextColor(getResources().getColor(R.color.red));  
                btn_index.setCompoundDrawablesWithIntrinsicBounds(null,   
                        getResources().getDrawable(R.drawable.index_indicator_s), null, null);  
                break;  
            case R.id.fragment_category:  
                fragment = new CategoryFragment();  
                actionbarTool.setUsuActionBar("分类");  
                btn_category.setTextColor(getResources().getColor(R.color.red));  
                btn_category.setCompoundDrawablesWithIntrinsicBounds(null,   
                        getResources().getDrawable(R.drawable.category_indicator_s), null, null);  
                break;  
            case R.id.fragment_discovery:  
                fragment = new DiscoveryFragment();  
                actionbarTool.setUsuActionBar("发现");  
                btn_discovery.setTextColor(getResources().getColor(R.color.red));  
                btn_discovery.setCompoundDrawablesWithIntrinsicBounds(null,   
                        getResources().getDrawable(R.drawable.discovery_s), null, null);  
                break;  
            case R.id.fragment_cart:  
                fragment = new CartFragment();  
                actionbarTool.setUsuActionBar("我的购物车");  
                btn_cart.setTextColor(getResources().getColor(R.color.red));  
                btn_cart.setCompoundDrawablesWithIntrinsicBounds(null,   
                        getResources().getDrawable(R.drawable.cart_tab_icon_red), null, null);  
                break;  
            case R.id.fragment_my:  
                fragment = new MyFragment();  
                //actionbarTool.setUsuActionBar("我的");  
                btn_my.setTextColor(getResources().getColor(R.color.red));  
                btn_my.setCompoundDrawablesWithIntrinsicBounds(null,   
                        getResources().getDrawable(R.drawable.my_indicator_s), null, null);  
                break;  
            }  
            if (fragment != null) {  
                fragmentManager.beginTransaction()  
                        .replace(R.id.content, fragment).commit();  
            } else {  
                Log.e("MainActivity", "Error in creating fragment");  
            }  
  
        }  
  
    }  
      
    /** 
     * 初始化按钮样式 
     *  
     */  
    private void InitBg(){  
        btn_index.setTextColor(getResources().getColor(R.color.white));  
        btn_index.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.index_indicator), null, null);  
        btn_category.setTextColor(getResources().getColor(R.color.white));  
        btn_category.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.category_indicator), null, null);  
        btn_cart.setTextColor(getResources().getColor(R.color.white));  
        btn_cart.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.cart_tab_icon_dark), null, null);  
        btn_discovery.setTextColor(getResources().getColor(R.color.white));  
        btn_discovery.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.discovery), null, null);  
        btn_my.setTextColor(getResources().getColor(R.color.white));  
        btn_my.setCompoundDrawablesWithIntrinsicBounds(null,   
                getResources().getDrawable(R.drawable.my_indicator), null, null);  
    }  
      
    @Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        return true;  
    }  
  
}  


其他页面的fragment.类似,布局文件是空布局:

public class IndexFragment extends Fragment{  
  
    @Override  
    public void onActivityCreated(Bundle savedInstanceState) {  
        // TODO Auto-generated method stub  
        super.onActivityCreated(savedInstanceState);  
    }  
  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container,  
            Bundle savedInstanceState) {  
        return inflater.inflate(R.layout.fragment_index, container, false);  
    }  
  
      
      
}  

上面的主要代码是:switchFragment类,是切换fragment的,通过fragmentManager.beginTransaction().replace(R.id.content, fragment).commit();来替换当前的fragment,在替换的时候需要先使用InitBg()初始化按钮的颜色,在来改变按钮的字体颜色和图标。效果如下:

仿蘑菇街界面应用

今天我们来重点来仿照一下第一个底部菜单“爱逛”,首先我们来分解一下功能区域:

仿蘑菇街界面应用

1.功能区域分解

 (1) PageTabs左右切换菜单:这里我们使用第三方开源插件,不过需要自己进行修改,采用的是ViewPage进行页面的切换,左右滑动。

(2) 图片轮播:ViewGroup+Viewpage每一个ViewGroup存储一个按压效果的dot和一张图片,随着手指的滑动进行图片之间的切换,当然我们这里只使用五张图片。

(3)更新时间:这个不说了,就是设置时间,这里找不到图片我自己设定固定值,当然也可以通过代码设置。

(4)第一个列表,第二个列表,我们观察布局可知,下面2个布局的大小是分别占用了屏幕的一般,我们可以通过设置权值属性来设置大小,也就是

@Override  
    protected void onDraw(Canvas canvas) {  
        super.onDraw(canvas);  
  
        if (isInEditMode() || tabCount == 0) {  
            return;  
        }  
  
        final int height = getHeight();  
          
        // draw underline  
        rectPaint.setColor(underlineColor);  
        canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);  
  
        // draw indicator line  
        rectPaint.setColor(indicatorColor);  
  
        // default: line below current tab  
        View currentTab = tabsContainer.getChildAt(currentPosition);  
        float lineLeft = currentTab.getLeft();  
        float lineRight = currentTab.getRight();  
  
        // if there is an offset, start interpolating left and right coordinates between current and next tab  
        if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {  
  
            View nextTab = tabsContainer.getChildAt(currentPosition + 1);  
            final float nextTabLeft = nextTab.getLeft();  
            final float nextTabRight = nextTab.getRight();  
  
            lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);  
            lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);  
        }  
  
        canvas.drawRect(lineLeft+100, height - indicatorHeight, lineRight-100, height, rectPaint);  
  
        // draw divider  
  
        dividerPaint.setColor(dividerColor);  
        for (int i = 0; i < tabCount - 1; i++) {  
            View tab = tabsContainer.getChildAt(i);  
            canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);  
        }  
    }  

主要看第这一行

canvas.drawRect(lineLeft+100, height - indicatorHeight, lineRight-100, height, rectPaint);

这行lineLeft+100,lineRight-100设置左右两边同时减小100的长度。

设置IndexFragment代码,该代码就是“精选”菜单区域Fragment,代码如下:

public class ChildViewPager extends ViewPager {  
    public ChildViewPager(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        // TODO Auto-generated constructor stub  
    }  
  
    public ChildViewPager(Context context) {  
        super(context);  
        // TODO Auto-generated constructor stub  
    }  
  
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent arg0) {  
        // 当拦截触摸事件到达此位置的时候,返回true,  
        // 说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent  
        return true;  
    }  
  
  
    @Override  
    public boolean onTouchEvent(MotionEvent arg0) {  
        getParent().requestDisallowInterceptTouchEvent(true);  
        return super.onTouchEvent(arg0);  
    }  
}  

 代码很少,主要是这一句getParent().requestDisallowInterceptTouchEvent(true);

设置父控件不响应OnTouch事件,而是交给当前控件的onTouchEvent事件,从而阻止PageTabS的滑动,响应当前控件的滑动事件

最后这是ViewPage的数据适配器,添加图片和点,进行左右的移动,定义ImgaePagerAdapter适配器类,其代码如下:

public class ImgaePagerAdapter extends PagerAdapter {  
  
    ImageView[] mImageViews;  
  
    public ImgaePagerAdapter(ImageView[] mImageViews) {  
        this.mImageViews = mImageViews;  
    }  
    //获取要滑动的控件的数量  
    @Override  
    public int getCount() {  
        return Integer.MAX_VALUE;  
    }  
    //来判断显示的是否是同一张图片,这里我们将两个参数相比较返回即可    
    @Override  
    public boolean isViewFromObject(View arg0, Object arg1) {  
        return arg0 == arg1;  
    }  
    //PagerAdapter如果滑动的图片超出了缓存的范围,就会调用这个方法,将图片销毁    
    @Override  
    public void destroyItem(View container, int position, Object object) {  
          
        ((ViewPager) container).removeView(mImageViews[position  
                % mImageViews.length]);  
  
    }  
  
    /** 
     *循环读取图片,取余数 
     */  
    @Override  
    public Object instantiateItem(View container, int position) {  
        ((ViewPager) container).addView(mImageViews[position  
                % mImageViews.length], 0);  
        return mImageViews[position % mImageViews.length];  
    }  
  
}  

循环图片的代码主要放在了instantiateItem中,该事件负责将图片添加到容器中,并返回该图片视图,并且每次返回的图片为当前的位置和图片的总长度取余数,通过取余数从而判断是否进行循环。

在Fragment中调用如下的代码对数据适配器的绑定

viewPager.setAdapter(new ImgaePagerAdapter(mImageViews));

viewPager.setOnPageChangeListener(this);

(3)控件

这里的列表控件用来显示精选衣服基本信息,使用图片加文字的组合方式,我们第一反应想到的是GridView控件,这个想法是对的;可是我们注意到“精选”菜单的整个布局是使用ScrollView控件来控制上下一起移动的,如果单纯使用GridView控件的话,GridView控件在ScrollView中会显示不正常,所以我们应当自定义GirdView让它不能滑动,并且适配ScrollView控件的大小。定义MyGridView控件:

public class ImgaePagerAdapter extends PagerAdapter {  
  
    ImageView[] mImageViews;  
  
    public ImgaePagerAdapter(ImageView[] mImageViews) {  
        this.mImageViews = mImageViews;  
    }  
    //获取要滑动的控件的数量  
    @Override  
    public int getCount() {  
        return Integer.MAX_VALUE;  
    }  
    //来判断显示的是否是同一张图片,这里我们将两个参数相比较返回即可    
    @Override  
    public boolean isViewFromObject(View arg0, Object arg1) {  
        return arg0 == arg1;  
    }  
    //PagerAdapter如果滑动的图片超出了缓存的范围,就会调用这个方法,将图片销毁    
    @Override  
    public void destroyItem(View container, int position, Object object) {  
          
        ((ViewPager) container).removeView(mImageViews[position  
                % mImageViews.length]);  
  
    }  
  
    /** 
     *循环读取图片,取余数 
     */  
    @Override  
    public Object instantiateItem(View container, int position) {  
        ((ViewPager) container).addView(mImageViews[position  
                % mImageViews.length], 0);  
        return mImageViews[position % mImageViews.length];  
    }  
  
}  

在绘制GridView控件大小的时候,通过设置MeasureSpec.AT_MOST参数来指定到想要控件高度,通过onMeasure事件来绘制GridView。在XML布局中引用<com.blog.mogujie.tool.MyGridView ../>,布局文件代码太长就不贴出来了,待会在后面提供代码下载链接。

然后再定义该GridView文件的适配器,代码如下,注意GridView的优化设置:

public class GrdoneAdapter extends BaseAdapter{  
  
    private Context mContext;  
    private List<GrdOneInfo> mGrdOneInfoList;  
    public GrdoneAdapter(Context mContext,List<GrdOneInfo> mGrdOneInfoList){  
        this.mContext=mContext;  
        this.mGrdOneInfoList=mGrdOneInfoList;  
    }  
    @Override  
    public int getCount() {  
        // TODO Auto-generated method stub  
        return mGrdOneInfoList.size();  
    }  
  
    @Override  
    public Object getItem(int position) {  
        // TODO Auto-generated method stub  
        return position;  
    }  
  
    @Override  
    public long getItemId(int position) {  
        // TODO Auto-generated method stub  
        return position;  
    }  
  
    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        View view = convertView;  
        final ViewHolder holder;  
        if (convertView == null) {  
            view = ((Activity) mContext).getLayoutInflater().inflate(  
                    R.layout.item_grd1, parent, false);  
            holder = new ViewHolder();  
            holder.image = (ImageView) view.findViewById(R.id.grdimage);  
            holder.brife= (TextView) view.findViewById(R.id.brife1);  
            holder.price= (TextView) view.findViewById(R.id.price);  
            holder.marks= (TextView) view.findViewById(R.id.marks);  
            view.setTag(holder);  
        } else {  
            holder = (ViewHolder) view.getTag();  
        }  
        holder.image.setImageResource(mGrdOneInfoList.get(position).imagePath);  
        if(mGrdOneInfoList.get(position).brife.length()>50){  
            holder.brife.setText(mGrdOneInfoList.get(position).brife.subSequence(0, 30)+"...");  
        }else{  
            holder.brife.setText(mGrdOneInfoList.get(position).brife);  
        }  
        holder.price.setText(mGrdOneInfoList.get(position).price);  
        holder.marks.setText(mGrdOneInfoList.get(position).marksNum);  
        return view;  
    }  
  
    static class ViewHolder{  
        ImageView image;  
        TextView brife;  
        TextView price;  
        TextView marks;  
    }  
}  

最终效果如下:

 仿蘑菇街界面应用

资源地址为点击打开链接,今天到这里。

相关推荐