仿网易新闻APP底部菜单栏

来源:互联网 发布:nba20162017数据统计 编辑:程序博客网 时间:2024/04/30 22:02

看到这个标题,是不是有很多人觉得这好像很简单,答对了一半,这是很简单的一个功能,可以有许多的实现方法。我这里讲解的是谷歌提倡的方法。


我重点讲解我独特的底部菜单栏的实现方式,这种方法重用及效率很高,而且还设计选择器的应用及图片控件的选择模式。


好了,废话我就不多说了,上图开讲:



1.反编译APK


首先的第一个难题就是,我不是设计师,不会PS,我是程序员啊,我哪有这些图片?


请某位高大上的设计师帮你做图片,可以,分成的时候你会给设计师一大笔钱,你觉得划算?况且那些高大上的设计师,会给你设计这些简单的小图标,一般到设计师那个级别的都很高傲。别指望设计师会免费为你提供任何有价值的服务,这个世界就是这样,独立合作的APP开发工作室会给设计师一大笔金钱,靠朋友关系请来的设计师,你没给钱,你觉得人家会认真做图片给你?也就是个友情价。(其实我是羡慕设计师,因为任何东西都是先设计出来才做了,我是个自卑的程序员,感觉任何行业最牛逼的就是设计师了,所有行业都受制于设计师)。


获取免费你的你想要开发的图片最简单,也是程序员经常使用的方法就是反编译APK,下面我们就来看看,怎么反编译网易新闻APP得到你想要的图片。


Ⅰ下载apktool


最新下载地址:http://download.csdn.net/detail/liyuanjinglyj/9199453


将下载的APK解压到一个单独的文件夹,如下所示:



本人放在E盘根目录下:



apktool包含这三个东西,一定要注意,上面那些是我反编译的其他APK,不属于apktool的资源。


Ⅱ下载网易新闻APP


这个不用我多说,百度下载android版本的网易APK。将你下载的APK改名为简单的名字后放入刚才的apktool目录中,如下图:



Ⅲ反编译网易APK


点击开始菜单输入cmd,回车:



切换到apktool目录中:



输入如下命令:apktool d -f 【wangyi.apk】 -o 【liyuanjinglyj】


第一个括号为你修改后网易APK的名称,要加后缀APK,第二个括号为存放反编译后的文件名,该文件创建在当前目录中。


得到如下结果就算反编译成功了:



我们来看看我们得到了什么东西,打开反编译后的文件夹liyuanjinglyj:



我们需要的文件全部都在res中,图片,布局文件,动画等资源,就像你开发项目一样在res目录下的所有文件,smali为反编译后的编程代码,如果你没学过Dalvik虚拟机语法,你是看不懂的,如果你想了解这方面知识,想在一些apk中植入代码,可以参考图灵之Android软件安全与逆向分析,这不属于我这章要讲的内容,如果有需要,我可以开设专栏讲解。


下面我们进入E:\apktool\liyuanjinglyj\res\drawable-xxhdpi-v4下找到我们需要的网易底部菜单栏图片复制到我们的工程项目中的drawable下。一共是10张。


2.设计底部菜单栏


我们应该用什么做下面的菜单呢?是弄RadioButton?NO,这不仅要消除RadioButton的原点,还要增加其底部的文字等,而且一旦你受阻碍于一个控件,你是无法扩展你任何想要的特效的。


这里我们用五个layout布局包裹每个菜单的按钮文字,把控件分开来做,这样可以方便扩展你所需要的功能,用RadioButton可以实现,可是这已经最小的控件了,你如果需要扩展怎么扩展?显然我选择的效率还是功能,都比RadioButton要高级的多。


编程的时候要注意,别把代码写死,这样就没有扩展的可能了。


Ⅰ创建底部菜单栏布局文件bottom_title_layout.xml:



<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal" android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_weight="0">    <LinearLayout        android:id="@+id/bottom_new_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        android:orientation="vertical"        android:gravity="center"        android:layout_weight="1">        <ImageButton            android:layout_width="32dp"            android:layout_height="32dp"            android:duplicateParentState="true"            android:clickable="false"            android:scaleType="fitXY"            android:background="@drawable/news_selector_background" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:duplicateParentState="true"            android:clickable="false"            android:text="@string/bottom_title_news_string"            android:textColor="@color/bottom_title_text_color"/>    </LinearLayout>    <LinearLayout        android:id="@+id/bottom_read_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        android:orientation="vertical"        android:gravity="center"        android:layout_weight="1">        <ImageButton            android:layout_width="32dp"            android:layout_height="32dp"            android:duplicateParentState="true"            android:clickable="false"            android:scaleType="fitXY"            android:background="@drawable/read_selector_background"/>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:duplicateParentState="true"            android:clickable="false"            android:text="@string/bottom_title_read_string"            android:textColor="@color/bottom_title_text_color"/>    </LinearLayout>    <LinearLayout        android:id="@+id/bottom_video_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        android:orientation="vertical"        android:gravity="center"        android:layout_weight="1">        <ImageButton            android:layout_width="32dp"            android:layout_height="32dp"            android:duplicateParentState="true"            android:clickable="false"            android:scaleType="fitXY"            android:background="@drawable/video_selector_background" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:duplicateParentState="true"            android:clickable="false"            android:text="@string/bottom_title_video_string"            android:textColor="@color/bottom_title_text_color"/>    </LinearLayout>    <LinearLayout        android:id="@+id/bottom_lamp_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        android:orientation="vertical"        android:gravity="center"        android:layout_weight="1">        <ImageButton            android:layout_width="32dp"            android:layout_height="32dp"            android:duplicateParentState="true"            android:clickable="false"            android:scaleType="fitXY"            android:background="@drawable/lamp_selector_background" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:duplicateParentState="true"            android:clickable="false"            android:text="@string/bottom_title_lamp_string"            android:textColor="@color/bottom_title_text_color"/>    </LinearLayout>    <LinearLayout        android:id="@+id/bottom_pc_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:clickable="true"        android:orientation="vertical"        android:gravity="center"        android:layout_weight="1">        <ImageButton            android:layout_width="32dp"            android:layout_height="32dp"            android:duplicateParentState="true"            android:clickable="false"            android:scaleType="fitXY"            android:background="@drawable/pc_selector_background" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:duplicateParentState="true"            android:clickable="false"            android:text="@string/bottom_title_pc_string"            android:textColor="@color/bottom_title_text_color"/>    </LinearLayout></LinearLayout>



解释1:


每一个菜单都是用imageButton和TextView组成,用他们的父LinearLayout拦截他们的点击事件,这样子控件的可以同时相应他们的点击事件,


要想父控件相应子控件,而点击子控件就是跟点击父控件一样的效果的时候,必须同时设置三个属性,方可以实现此功能。


一是必须设置父控件可以被点击,如下:


android:clickable="true"


二是必须设置子控件会跟随父控件的状态,也就是父控件被点击,子控件也是被点击:


android:duplicateParentState="true"


三是必须设置子控件不可以被点击,这样保证单独点击子控件,子控件会将点击事件传递给父控件处理,不然点击事件会被子控件拦截,那么另一个子控件就不会响应了:


android:clickable="false"


解释2:


这里面所有子控件都有选择器,我们先从文字选择器开始讲解,因为文字被点击,被按下,被选择,这五个的效果都是一样的,所以这里只用了一个选择器,也就是bottom_title_text_color.xml:


<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:state_selected="true" android:color="#CD0000" />    <item android:state_focused="true" android:color="#CD0000" />    <item android:state_pressed="true" android:color="#CD0000"/>    <item android:color="#89683B" /></selector>


android:state_selected="true"此为选中时候的文字颜色

android:state_focused="true"此为非触摸式下获取焦点时候的文字颜色,也就是你按住屏幕某个位子,将手指移动到该处时候的反映。

android:state_pressed="true"此为按下时候的文字颜色

最下面无任何前提的属于默认颜色。这里上三个偏红,默认偏灰。


文字颜色选择器应该放到res/color中。


这里有必要解释一下的是冲突,什么是选择冲突?


当你点击某个按钮的时候,这个时候此按钮不仅被按下,同时为选中,也获取到了焦点。那么三者合一对于文字来说很好理解,这三个一样嘛,无所谓,可当这是图片选择器的时候,三种选择样式不一样就会有冲突,这个是怎么解决的呢?


默认程序是选择你选择器中写的最前面的那个作为显示的,所以你编程的时候,最好考虑清楚冲突的问题,免得写程序的时候,明明写的选择器却没有达到你想要的效果,你又不知道原因。


解释3:


新闻背景选择器news_selector_background.xml代码:


<selector xmlns:android="http://schemas.android.com/apk/res/android">    <item android:state_pressed="true" android:drawable="@drawable/news_red"/>    <item android:state_selected="true" android:drawable="@drawable/news_red"/>    <item android:state_enabled="true" android:drawable="@drawable/news_black"/></selector>


其他背景选择器和这个一样,就不一一描述了。


上面讲解过文字颜色选择器,这个图片选择器原理是一模一样的。


解释4:


android:scaleType="fitXY"


将图片边界缩放,以适应视图边界时的可选项。


在视图中使图像居中,不执行缩放。 在 XML 中可以使用的语法: android:scaleType="center"。

均衡的缩放图像(保持图像原始比例),使图片的两个坐标(宽、高)都大于等于 相应的视图坐标(负的内边距)。图像则位于视图的中央。 在 XML 中可以使用的语法:android:scaleType="centerCrop"。

均衡的缩放图像(保持图像原始比例),使图片的两个坐标(宽、高)都小于等于 相应的视图坐标(负的内边距)。图像则位于视图的中央。 在 XML 中可以使用的语法:android:scaleType="centerInside"。

使用 CENTER 方式缩放图像。 在 XML 中可以使用的语法: android:scaleType="fitCenter"。

使用 END 方式缩放图像。 在 XML 中可以使用的语法: android:scaleType="fitEnd"。

使用 START 方式缩放图像。 在 XML 中可以使用的语法:android:scaleType="fitStart"。

使用 FILL 方式缩放图像。 在 XML 中可以使用的语法: android:scaleType="fitXY"。

绘制时,使用图像矩阵方式缩放。图像矩阵可以通过 setImageMatrix(Matrix) 设置。在 XML 中可以使用的语法: android:scaleType="matrix"。


这里很显然,是让图片适应我所设置的大小。


解释5:


注意在Android编程是要考虑手机尺寸问题的,什么单位会自适应手机尺寸呢?


dp:图片自适应手机尺寸,图片只要设置这个属性,在大手机上,小手机上会等比例缩放。


sp:文字自适应手机尺寸,文字只要设置这个属性,在大手机上,小手机上会等比例缩放。


如果手机尺寸差别大到平板和手机的区别,这个时候是建立特定手机图片文件夹sw320dp、sw600dp、sw720dp等屏幕的基本尺寸。


解释6:




新闻我叫news,阅读我叫read,视听我叫video,发现我叫lamp(灯泡),我叫pc。防止误解。




Ⅱ创建5个用于切换的fragment





分别创建它们的布局文件,用于显示和区分,如下:





每个布局里面放个TextView用于区分就可以了,今天重点讲解实现,并不具体实现功能。


如下代码:


public class NewFragment extends Fragment {    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view=inflater.inflate(R.layout.news_fragment_main_layout,container,false);        return view;    }}

布局代码:

<?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">    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textSize="32sp"        android:text="wo shi news"/></LinearLayout>


Ⅲ主布局文件中加入4个fragment


主布局代码如下activity_main:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <fragment        android:id="@+id/fragement_news"        android:name="com.example.liyuanjing.btmmenufragment.NewFragment"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_weight="1" />    <fragment        android:id="@+id/fragement_read"        android:name="com.example.liyuanjing.btmmenufragment.ReadFragment"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_weight="1" />    <fragment        android:id="@+id/fragement_video"        android:name="com.example.liyuanjing.btmmenufragment.VideoFragment"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_weight="1" />    <fragment        android:id="@+id/fragement_lamp"        android:name="com.example.liyuanjing.btmmenufragment.LampFragment"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_weight="1" />    <fragment        android:id="@+id/fragement_pc"        android:name="com.example.liyuanjing.btmmenufragment.PcFragment"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:layout_weight="1" />    <include layout="@layout/bottom_title_layout"/></LinearLayout>


这个里面不需要解释相信大家都看的懂了,


现在就是MainActivity里面的代码了,要想使用fragment,必须使MainActivity继承自FragmentActivity里面。


其原因是里面包含了Fragment运作的FragmentManager接口的实现类 FragmentManagerImpl ,由这个类管理所有Fragment的显示、隐藏。而fragmentTransaction对fragment进行添加,替换移除等功能。


使用fragmentManager.beginTransaction()获取FragmentTransaction实例,FragmentManager是在activity中与fragment对象进行互操作的接口。在继承自FragmentActivity中通过getSupportFragmentManager();获得。


主MainActivity.class代码如下:


import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.os.Bundle;import android.view.View;import android.widget.LinearLayout;public class MainActivity extends FragmentActivity implements View.OnClickListener{    private LinearLayout newsLayout;    private LinearLayout readLayout;    private LinearLayout videoLayout;    private LinearLayout lampLayout;    private LinearLayout pcLayout;    private Fragment[] mFragments;    private FragmentManager fragmentManager;    private FragmentTransaction fragmentTransaction;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        this.newsLayout = (LinearLayout) findViewById(R.id.bottom_new_layout);        this.readLayout = (LinearLayout) findViewById(R.id.bottom_read_layout);        this.videoLayout = (LinearLayout) findViewById(R.id.bottom_video_layout);        this.lampLayout = (LinearLayout) findViewById(R.id.bottom_lamp_layout);        this.pcLayout = (LinearLayout) findViewById(R.id.bottom_pc_layout);        this.newsLayout.setOnClickListener(this);        this.readLayout.setOnClickListener(this);        this.videoLayout.setOnClickListener(this);        this.lampLayout.setOnClickListener(this);        this.pcLayout.setOnClickListener(this);        this.newsLayout.setSelected(true);//选择器就是这样用的,设置false,或true来改变选择        mFragments = new Fragment[5];        this.fragmentManager = getSupportFragmentManager();//获取fragmentManager实例,通过fragmentManager管理fragment        mFragments[0] = fragmentManager.findFragmentById(R.id.fragement_news);//获取fragment实例        mFragments[1] = fragmentManager.findFragmentById(R.id.fragement_read);        mFragments[2] = fragmentManager.findFragmentById(R.id.fragement_video);        mFragments[3] = fragmentManager.findFragmentById(R.id.fragement_lamp);        mFragments[4] = fragmentManager.findFragmentById(R.id.fragement_pc);        fragmentTransaction = fragmentManager.beginTransaction()//获取fragmentTransaction实例                .hide(mFragments[0]).hide(mFragments[1]).hide(mFragments[2]).hide(mFragments[3]).hide(mFragments[4]);//通过hide显示隐藏当前fragment        fragmentTransaction.show(mFragments[0]).commit();//通过show显示当前fragment,通过commit()提交刚才的操作。commit()和数据库的相似,你可以用fragmentT//ransaction做一大堆操作后一起提交。    }    @Override    public void onClick(View v) {        fragmentTransaction = fragmentManager.beginTransaction()                .hide(mFragments[0]).hide(mFragments[1]).hide(mFragments[2]).hide(mFragments[3]).hide(mFragments[4]);        switch (v.getId()) {            case R.id.bottom_new_layout:                this.newsLayout.setSelected(true);//设置选择器                this.readLayout.setSelected(false);                this.videoLayout.setSelected(false);                this.lampLayout.setSelected(false);                this.pcLayout.setSelected(false);                fragmentTransaction.show(mFragments[0]).commit();//显示第一fragment                break;            case R.id.bottom_read_layout:                this.newsLayout.setSelected(false);                this.readLayout.setSelected(true);                this.videoLayout.setSelected(false);                this.lampLayout.setSelected(false);                this.pcLayout.setSelected(false);                fragmentTransaction.show(mFragments[1]).commit();                break;            case R.id.bottom_video_layout:                this.newsLayout.setSelected(false);                this.readLayout.setSelected(false);                this.videoLayout.setSelected(true);                this.lampLayout.setSelected(false);                this.pcLayout.setSelected(false);                fragmentTransaction.show(mFragments[2]).commit();                break;            case R.id.bottom_lamp_layout:                this.newsLayout.setSelected(false);                this.readLayout.setSelected(false);                this.videoLayout.setSelected(false);                this.lampLayout.setSelected(true);                this.pcLayout.setSelected(false);                fragmentTransaction.show(mFragments[3]).commit();                break;            case R.id.bottom_pc_layout:                this.newsLayout.setSelected(false);                this.readLayout.setSelected(false);                this.videoLayout.setSelected(false);                this.lampLayout.setSelected(false);                this.pcLayout.setSelected(true);                fragmentTransaction.show(mFragments[4]).commit();                break;            default:                break;        }    }}


上面就是今天网易底部菜单栏的实现,很简单是不是。不过设计的知识点有点多,单个知识点并没有细细讲解,只是简单的提到,如有需要还请百度你要的详细答案。


本文的源代码在:


http://download.csdn.net/detail/liyuanjinglyj/9200825


测试结果:





3 0