zoukankan      html  css  js  c++  java
  • 仿9GAG制作过程(一)

    有话要说:

    准备开始学习Android应用程序的一个完整的设计过程。准备做一个仿9GAG的APP,前端界面设计+后台数据爬虫+后台接口设计,整个流程体验一遍。今天准备先把前端界面的框架给完成了。

    成果图:

    布局代码:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:app="http://schemas.android.com/apk/res-auto"
     4     xmlns:tools="http://schemas.android.com/tools"
     5     android:id="@+id/drawer_layout"
     6     android:layout_width="match_parent"
     7     android:layout_height="match_parent"
     8     android:fitsSystemWindows="true"
     9     tools:openDrawer="start">
    10 
    11     <include
    12         layout="@layout/activity_main_appbar"
    13         android:layout_width="match_parent"
    14         android:layout_height="match_parent" />
    15 
    16     <android.support.design.widget.NavigationView
    17         android:id="@+id/nav_view"
    18         android:layout_width="wrap_content"
    19         android:layout_height="match_parent"
    20         android:layout_gravity="start"
    21         android:fitsSystemWindows="false"
    22         app:headerLayout="@layout/activity_main_drawer_head"
    23         app:menu="@menu/activity_main_drawer_menu"
    24         android:theme="@style/MenuTextStyle"
    25          />
    26 
    27 </android.support.v4.widget.DrawerLayout>

    主活动用了DrawerLayout的布局方式,通过设置DrawerLayout的openDrawer属性以及NavigationView的gravity属性来实现左侧的测拉区域。

    下面来看看NavigationView的头部布局以及menu的布局:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <RelativeLayout
     3     xmlns:android="http://schemas.android.com/apk/res/android"
     4     android:layout_width="match_parent"
     5     android:layout_height="?attr/actionBarSize"
     6     android:background="@color/background">
     7 
     8     <RelativeLayout
     9         android:layout_width="match_parent"
    10         android:layout_height="match_parent">
    11         <de.hdodenhof.circleimageview.CircleImageView
    12             android:id="@+id/circleImageView"
    13             android:layout_width="28dp"
    14             android:layout_height="28dp"
    15             android:layout_centerVertical="true"
    16             android:layout_marginLeft="14dp"/>
    17 
    18         <TextView
    19             android:layout_width="wrap_content"
    20             android:layout_height="wrap_content"
    21             android:text="懒星人"
    22             android:layout_centerVertical="true"
    23             android:layout_marginLeft="14dp"
    24             android:layout_toRightOf="@id/circleImageView"
    25             android:textColor="@color/colorPrimary"/>
    26         <ImageView
    27             android:id="@+id/imageView"
    28             android:layout_width="wrap_content"
    29             android:layout_height="wrap_content"
    30             android:src="@drawable/ic_settings_gray_24dp"
    31             android:layout_centerVertical="true"
    32             android:layout_marginRight="14dp"
    33             android:layout_alignParentRight="true"/>
    34     </RelativeLayout>
    35 
    36     <View
    37         android:layout_alignParentBottom="true"
    38         android:layout_width="match_parent"
    39         android:layout_height="1dp"
    40         android:background="?android:listDivider"
    41         />
    42 </RelativeLayout>

    这里用到了CircleImageView组件来实现图片缩放裁剪成圆形,作为左上角头像的布局。并且由于头部的布局与menu的布局之间没有直接的分割线,就用View来实现了一个分割线。

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     tools:showIn="navigation_view">
     5 
     6     <group
     7         android:id="@+id/group1"
     8         android:checkableBehavior="single">
     9         <item
    10             android:id="@+id/nav_home"
    11             android:icon="@drawable/ic_home_gray_24dp"
    12             android:title="@string/home" />
    13         <item
    14             android:id="@+id/nav_notifications"
    15             android:icon="@drawable/ic_notifications_gray_24dp"
    16             android:title="@string/notifications" />
    17     </group>
    18 
    19     <group android:id="@+id/group2">
    20         <item
    21             android:id="@+id/nav_share"
    22             android:icon="@drawable/ic_share_gray_24dp"
    23             android:title="@string/share" />
    24         <item
    25             android:id="@+id/nav_send"
    26             android:icon="@drawable/ic_send_gray_24dp"
    27             android:title="@string/send" />
    28     </group>
    29 
    30 </menu>

    左侧menu的布局和主menu实现方式一致,通过menu的配置文件来实现。

    在这里遇到了两个问题:

    • 左侧menu字体不是粗体,但是需要粗体
    • 左侧menu布局的图标和文字之间的间隔太大

    第一个问题通过给NavigationView设置了主题,主题的主要意义就是加粗字体,如下代码:

    <style name="MenuTextStyle">
            <item name="android:textStyle">bold</item>
    </style>

    第二个问题,通过阅读NavigationView的源码逐步找到了item的布局文件,布局文件为design_navigation_menu_item.xml,于是将布局文件复制到layout下,并将drawablePadding改成了20dp,如下代码:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     3 
     4     <CheckedTextView
     5         android:id="@+id/design_menu_item_text"
     6         android:layout_width="0dp"
     7         android:layout_height="match_parent"
     8         android:layout_weight="1"
     9         android:drawablePadding="20dp"
    10         android:gravity="center_vertical|start"
    11         android:maxLines="1"
    12         android:textAppearance="@style/TextAppearance.AppCompat.Body2"/>
    13 
    14     <ViewStub
    15         android:id="@+id/design_menu_item_action_area_stub"
    16         android:inflatedId="@+id/design_menu_item_action_area"
    17         android:layout="@layout/design_menu_item_action_area"
    18         android:layout_width="wrap_content"
    19         android:layout_height="match_parent"/>
    20 
    21 </merge>

    插一个知识点:

    可以直接通过Android Studio来生成需要用到的图标,这里的图标我都是直接通过Android Studio生成的,生成步骤如下:

    接下来说一下主页面的实现,是通过TabLayout+ViewPage的方式来实现的,先来看代码:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:app="http://schemas.android.com/apk/res-auto"
     4     xmlns:tools="http://schemas.android.com/tools"
     5     android:layout_width="match_parent"
     6     android:layout_height="match_parent"
     7     tools:context=".activity.MainActivity">
     8 
     9     <android.support.design.widget.AppBarLayout
    10         android:layout_width="match_parent"
    11         android:layout_height="wrap_content"
    12         android:theme="@style/AppTheme.AppBarOverlay">
    13 
    14         <android.support.v7.widget.Toolbar
    15             android:id="@+id/toolbar"
    16             android:layout_width="match_parent"
    17             android:layout_height="?attr/actionBarSize"
    18             android:background="?attr/colorPrimary"
    19             app:layout_scrollFlags="scroll|enterAlways"
    20             app:popupTheme="@style/AppTheme.PopupOverlay" />
    21 
    22         <android.support.design.widget.TabLayout
    23             android:id="@+id/tabLayout"
    24             android:layout_width="match_parent"
    25             android:layout_height="wrap_content"
    26             app:tabBackground="@color/background"
    27             app:tabIndicatorColor="@color/colorPrimary"
    28             app:tabTextColor="@color/defaultColor"
    29             app:tabSelectedTextColor="@color/colorPrimary"
    30             app:tabTextAppearance="@style/TabText"/>
    31 
    32     </android.support.design.widget.AppBarLayout>
    33 
    34     <android.support.v4.view.ViewPager
    35         android:id="@+id/viewPage"
    36         android:layout_width="match_parent"
    37         android:layout_height="match_parent"
    38         app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
    39 
    40 </android.support.design.widget.CoordinatorLayout>

    这里需要注意一下几个知识点:

    • 通过给Toolbar的layout_scrollFlags属性设置scroll|enterAlways并且给ViewPager设置layout_behavior属性来实现滑动的时候Toolbar消失。即当设置layout_behavior的组件滑动时设置layout_scrollFlags的组件会移出屏幕
    •  给TabLayout的tabTextAppearance设置一个字体样式来实现Tab页加粗效果

    下面主要来说一下activity部分的代码,先上代码:

      1 package com.example.lanxingren.imitating9gag.activity;
      2 
      3 import android.os.Bundle;
      4 import android.support.design.widget.NavigationView;
      5 import android.support.design.widget.TabLayout;
      6 import android.support.v4.app.Fragment;
      7 import android.support.v4.view.GravityCompat;
      8 import android.support.v4.view.ViewPager;
      9 import android.support.v4.widget.DrawerLayout;
     10 import android.support.v7.app.ActionBarDrawerToggle;
     11 import android.support.v7.app.AppCompatActivity;
     12 import android.support.v7.widget.Toolbar;
     13 import android.view.Menu;
     14 import android.view.MenuItem;
     15 
     16 import com.example.lanxingren.imitating9gag.R;
     17 import com.example.lanxingren.imitating9gag.adapter.MyFragmentPagerAdapter;
     18 import com.example.lanxingren.imitating9gag.fragment.HomeFragment;
     19 import com.squareup.picasso.Picasso;
     20 
     21 import java.util.ArrayList;
     22 import java.util.List;
     23 
     24 import butterknife.BindView;
     25 import butterknife.ButterKnife;
     26 import de.hdodenhof.circleimageview.CircleImageView;
     27 
     28 public class MainActivity extends AppCompatActivity
     29         implements NavigationView.OnNavigationItemSelectedListener {
     30 
     31     @BindView(R.id.toolbar)
     32     Toolbar toolbar;
     33     @BindView(R.id.drawer_layout)
     34     DrawerLayout drawer;
     35     @BindView(R.id.nav_view)
     36     NavigationView navigationView;
     37     @BindView(R.id.tabLayout)
     38     TabLayout tabLayout;
     39     @BindView(R.id.viewPage)
     40     ViewPager viewPager;
     41 
     42     @Override
     43     protected void onCreate(Bundle savedInstanceState) {
     44         super.onCreate(savedInstanceState);
     45 
     46         setContentView(R.layout.activity_main);
     47 
     48         ButterKnife.bind(this);
     49 
     50         //设置ActionBar
     51         setSupportActionBar(toolbar);
     52 
     53         //设置DrawerLayout的监听事件,其中后两个参数是给残障人士的语音
     54         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
     55                 this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
     56         //设置左上角的三杠图标
     57         toggle.syncState();
     58         drawer.addDrawerListener(toggle);
     59 
     60         //设置抽屉的监听事件
     61         navigationView.setNavigationItemSelectedListener(this);
     62 
     63         //直接findViewById会导致NPE,抽屉head部分的头像
     64         CircleImageView circleImageView = navigationView.getHeaderView(0)
     65                 .findViewById(R.id.circleImageView);
     66         Picasso.with(this).load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1527745766743&di=c24134fe5233902ca1a60a8665c30a35&imgtype=0&src=http%3A%2F%2Fimg1.sc115.com%2Fuploads%2Fsc%2Fjpg%2F144%2F18628.jpg")
     67                 .into(circleImageView);
     68 
     69         //定义viewPage的适配器
     70         List<Fragment> fragments = new ArrayList();
     71         fragments.add(new HomeFragment());
     72         fragments.add(new HomeFragment());
     73         MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments);
     74 
     75         viewPager.setAdapter(adapter);
     76         tabLayout.setupWithViewPager(viewPager);
     77     }
     78 
     79     @Override
     80     public void onBackPressed() {
     81         if (drawer.isDrawerOpen(GravityCompat.START)) {
     82             drawer.closeDrawer(GravityCompat.START);
     83         } else {
     84             super.onBackPressed();
     85         }
     86     }
     87 
     88     /**
     89      * 右上角按钮图标
     90      * @param menu
     91      * @return
     92      */
     93     @Override
     94     public boolean onCreateOptionsMenu(Menu menu) {
     95         getMenuInflater().inflate(R.menu.main, menu);
     96         return true;
     97     }
     98 
     99     //右上角按钮点击事件
    100     @Override
    101     public boolean onOptionsItemSelected(MenuItem item) {
    102         return super.onOptionsItemSelected(item);
    103     }
    104 
    105     //左侧抽屉menu点击事件
    106     @SuppressWarnings("StatementWithEmptyBody")
    107     @Override
    108     public boolean onNavigationItemSelected(MenuItem item) {
    109         int id = item.getItemId();
    110 
    111         if (id == R.id.nav_home) {
    112 
    113         } else if (id == R.id.nav_notifications) {
    114 
    115         } else if (id == R.id.nav_send) {
    116 
    117         } else if (id == R.id.nav_share) {
    118 
    119         }
    120 
    121         drawer.closeDrawer(GravityCompat.START);
    122         return true;
    123     }
    124 
    125 }

    知识点:

    • 用了ButterKnife而不是findViewById来获取组件
    • 用了Picasso来加载网络图片,头像以及内部都是通过这种方式来加载的
    • 定义了MyFragmentPagerAdapter适配器来实现ViewPage的布局

    MyFragmentPagerAdapter内部的数据实际上为HomeFragment,而该Fragment的布局实际上只是一个简单的RecyclerView,下面上HomeFragment的代码:

     1 package com.example.lanxingren.imitating9gag.fragment;
     2 
     3 import android.os.Bundle;
     4 import android.support.v4.app.Fragment;
     5 import android.support.v7.widget.LinearLayoutManager;
     6 import android.support.v7.widget.RecyclerView;
     7 import android.view.LayoutInflater;
     8 import android.view.View;
     9 import android.view.ViewGroup;
    10 
    11 import com.example.lanxingren.imitating9gag.R;
    12 import com.example.lanxingren.imitating9gag.adapter.NewsAdapter;
    13 import com.example.lanxingren.imitating9gag.bean.NewsBean;
    14 
    15 import java.util.ArrayList;
    16 import java.util.List;
    17 
    18 /**
    19  */
    20 public class HomeFragment extends Fragment {
    21 
    22     @Override
    23     public void onCreate(Bundle savedInstanceState) {
    24         super.onCreate(savedInstanceState);
    25     }
    26 
    27     @Override
    28     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    29                              Bundle savedInstanceState) {
    30         return inflater.inflate(R.layout.fragment_home, container, false);
    31     }
    32 
    33     @Override
    34     public void onStart() {
    35         super.onStart();
    36         List<NewsBean> newsBeans = new ArrayList<NewsBean>();
    37 
    38         for (int i = 0; i < 30; i++) {
    39             newsBeans.add(new NewsBean("这是第 " + Integer.toString(i+1) + " 条有趣的段子!",
    40                     "http://ws4.sinaimg.cn/mw600/6c560b83ly1fruncq3z03j20ks0rs41b.jpg", 0));
    41         }
    42 
    43         LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
    44 
    45         RecyclerView recyclerView = getView().findViewById(R.id.recyclerView);
    46         recyclerView.setAdapter(new NewsAdapter(newsBeans));
    47         recyclerView.setLayoutManager(linearLayoutManager);
    48     }
    49 }

    RecyrView用了NewsAdapter适配器,适配器代码如下:

     1 package com.example.lanxingren.imitating9gag.adapter;
     2 
     3 import android.content.Context;
     4 import android.support.annotation.NonNull;
     5 import android.support.v7.widget.CardView;
     6 import android.support.v7.widget.RecyclerView;
     7 import android.view.LayoutInflater;
     8 import android.view.View;
     9 import android.view.ViewGroup;
    10 import android.widget.ImageView;
    11 import android.widget.TextView;
    12 
    13 import com.example.lanxingren.imitating9gag.R;
    14 import com.example.lanxingren.imitating9gag.bean.NewsBean;
    15 import com.squareup.picasso.Picasso;
    16 
    17 import java.util.List;
    18 
    19 public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsHolder> {
    20 
    21     private List<NewsBean> myNewsList;
    22     private Context myContext;
    23 
    24     static class NewsHolder extends RecyclerView.ViewHolder {
    25         CardView cardView;
    26         TextView textView;
    27         ImageView imageView;
    28 
    29         private NewsHolder (View view) {
    30             super(view);
    31             cardView = (CardView) view;
    32             textView = view.findViewById(R.id.item_text);
    33             imageView = view.findViewById(R.id.item_image);
    34         }
    35     }
    36 
    37     public NewsAdapter (List<NewsBean> newsList) {
    38         this.myNewsList = newsList;
    39     }
    40 
    41     @Override
    42     public int getItemCount() {
    43         int count = 0;
    44         if (myNewsList != null) {
    45             count = myNewsList.size();
    46         }
    47         return count;
    48     }
    49 
    50     @NonNull
    51     @Override
    52     public NewsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    53         if (myContext == null) {
    54             myContext = parent.getContext();
    55         }
    56         View view = LayoutInflater.from(myContext).inflate(R.layout.item_news, parent, false);
    57         return new NewsHolder(view);
    58     }
    59 
    60     @Override
    61     public void onBindViewHolder(@NonNull NewsHolder holder, int position) {
    62         NewsBean newsBean = myNewsList.get(position);
    63         holder.textView.setText(newsBean.getTitle());
    64 
    65         int screenWidth = myContext.getResources()
    66                 .getDisplayMetrics()
    67                 .widthPixels;
    68         Picasso.with(myContext)
    69                 .load(newsBean.getPicUrl())
    70                 .resize(screenWidth, 0)
    71                 .into(holder.imageView);
    72     }
    73 }

    每一项的布局为item_news,一会儿看具体布局。在onBindViewHolder中给布局的textView设置了文字,给imageView设置了图片。

    之前看别人的博客,经常会在适配器中定义一个myContext,我一直觉得没什么用。但是在这次实际编写适配器的过程中,发现了myContext还是有很多地方要用到的。

    下面来看看item_news的布局,代码如下:

      1 <?xml version="1.0" encoding="utf-8"?>
      2 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
      3     android:layout_width="match_parent"
      4     android:layout_height="wrap_content"
      5     xmlns:app="http://schemas.android.com/apk/res-auto"
      6     android:layout_marginVertical="10dp"
      7     app:cardCornerRadius="0dp"
      8     android:elevation="0dp">
      9     <LinearLayout
     10         android:layout_width="match_parent"
     11         android:layout_height="wrap_content"
     12         android:orientation="vertical"
     13         >
     14         <RelativeLayout
     15             android:layout_width="match_parent"
     16             android:layout_height="40dp"
     17             android:gravity="center">
     18             <TextView
     19                 android:id="@+id/item_text"
     20                 android:textStyle="bold"
     21                 android:textColor="@color/colorPrimary"
     22                 android:layout_width="wrap_content"
     23                 android:layout_height="wrap_content"
     24                 android:layout_alignParentLeft="true"
     25                 android:layout_marginLeft="14dp"/>
     26             <ImageView
     27                 android:layout_width="wrap_content"
     28                 android:layout_height="wrap_content"
     29                 android:layout_alignParentRight="true"
     30                 android:src="@drawable/ic_expand_more_gray_24dp"
     31                 android:layout_marginRight="14dp"/>
     32         </RelativeLayout>
     33         <ImageView
     34             android:id="@+id/item_image"
     35             android:layout_width="match_parent"
     36             android:layout_height="wrap_content"
     37             android:scaleType="fitCenter"/>
     38         <LinearLayout
     39             android:layout_width="match_parent"
     40             android:layout_height="40dp"
     41             android:orientation="horizontal"
     42             android:gravity="center">
     43             <LinearLayout
     44                 android:layout_width="0dp"
     45                 android:layout_weight="1"
     46                 android:layout_height="wrap_content"
     47                 android:orientation="horizontal">
     48                 <ImageView
     49                     android:layout_width="0dp"
     50                     android:layout_weight="1"
     51                     android:layout_height="wrap_content"
     52                     android:src="@drawable/ic_thumb_up_gray_24dp"
     53                     android:scaleType="fitEnd"/>
     54                 <TextView
     55                     android:layout_width="0dp"
     56                     android:layout_weight="1"
     57                     android:layout_height="wrap_content"
     58                     android:gravity="center"
     59                     android:text="5k"/>
     60                 <ImageView
     61                     android:layout_width="0dp"
     62                     android:layout_weight="1"
     63                     android:layout_height="wrap_content"
     64                     android:src="@drawable/ic_thumb_down_gray_24dp"
     65                     android:scaleType="fitStart"/>
     66             </LinearLayout>
     67 
     68             <View
     69                 android:layout_width="1dp"
     70                 android:layout_height="20dp"
     71                 android:background="?android:listDivider"/>
     72 
     73             <LinearLayout
     74                 android:layout_width="0dp"
     75                 android:layout_weight="1"
     76                 android:layout_height="wrap_content"
     77                 android:gravity="center">
     78                 <ImageView
     79                     android:layout_width="0dp"
     80                     android:layout_weight="1"
     81                     android:layout_height="wrap_content"
     82                     android:src="@drawable/ic_comment_gray_24dp"
     83                     android:scaleType="fitEnd"
     84                     android:paddingRight="5dp"/>
     85                 <TextView
     86                     android:layout_width="0dp"
     87                     android:layout_weight="1"
     88                     android:layout_height="wrap_content"
     89                     android:gravity="left"
     90                     android:text="46"
     91                     android:paddingLeft="5dp"/>
     92             </LinearLayout>
     93 
     94             <View
     95                 android:layout_width="1dp"
     96                 android:layout_height="20dp"
     97                 android:background="?android:listDivider"/>
     98 
     99             <LinearLayout
    100                 android:layout_width="0dp"
    101                 android:layout_weight="1"
    102                 android:layout_height="wrap_content"
    103                 android:gravity="center">
    104                 <ImageView
    105                     android:layout_width="0dp"
    106                     android:layout_weight="1"
    107                     android:layout_height="wrap_content"
    108                     android:src="@drawable/ic_share_gray_24dp"
    109                     android:scaleType="fitEnd"
    110                     android:paddingRight="5dp"/>
    111                 <TextView
    112                     android:layout_width="0dp"
    113                     android:layout_weight="1"
    114                     android:layout_height="wrap_content"
    115                     android:gravity="left"
    116                     android:text="分享"
    117                     android:paddingLeft="5dp"/>
    118             </LinearLayout>
    119         </LinearLayout>
    120     </LinearLayout>
    121 
    122 </android.support.v7.widget.CardView>

    每一项使用的是卡片式布局,使用了官方的CardView组件。

    通过设置cardCornerRadius来设置圆角弧度为0,使得卡片为正矩形。

    ImageView的scaleType的意思是图片如何填充,其中fitCenter为居中填充,fitStart为左对齐填充,fitEnd为右对齐填充。

    写的比较仓促,如有疑问或者错误的地方欢迎留言指正。

  • 相关阅读:
    青春如同奔流的江河,一去不回来不及道别
    关于RESOURCE_SEMAPHORE等待类型
    sql server 2008 The fulltext filter daemon host (FDHost) process has stopped abnormally.
    [转]SQL Server 2008 R2 Pricing
    64bit sql server 2008 sp1 使用lock pages in memory 具体操作
    [转]Fun with Locked Pages, AWE, Task Manager, and the Working Set…
    关掉和开启win7的Windows Search服务
    How to recreate the msdb database in SQL Server 2005
    Kimberly L. Tripp的sp_helpindex2 for sql server 2005/2008,并修复了for sqk2k8的一个bug
    表无索引为什么sp_spaceused 中的index_size不为0
  • 原文地址:https://www.cnblogs.com/lanxingren/p/9123158.html
Copyright © 2011-2022 走看看