高仿知乎日报(二)[主页面]

by seaicelin
转载请注明出处:http://blog.csdn.net/amd123456789/article/details/52292623

主页面

主页面从上到下由DrawerLayout + ToolBar + SwipeRefreshLayout + RecylerView + HomeProtocol组成。

  • DrawerLayout : 用于实现抽屉效果的侧边栏菜单。
  • ToolBar : 用于实现标题栏,类似以前ActionBar。
  • SwipeRefreshLayout:用于实现下拉刷新效果,直接包裹着RecylerView。
  • RecylerView:主要用于显示日报的列表的ListView。

先来一睹主页面的效果(不会搞GIF图,并且手上是平板!),也就是这篇文章所要实现的效果图:

这里写图片描述


DrawerLayout 主页面和侧边栏抽屉效果

DrawerLayout是V4包提供的一个widget,需要在activity和布局文件里边操作来实现这个效果。布局界面包含侧边栏界面和主页面界面。布局文件按照规定的格式书写如下:
activity_content.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 内容显示   -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:orientation="vertical">

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar"></include>
        //包裹下拉刷新
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipe_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/light_grey">
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyle_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@color/light_grey">
                </android.support.v7.widget.RecyclerView>
            </RelativeLayout>
        </android.support.v4.widget.SwipeRefreshLayout>
    </LinearLayout>
    <!--侧边栏-->
    <LinearLayout
        android:id="@+id/drawer_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"//可以设置左边还是右边侧滑
        android:background="@android:color/white"
        android:orientation="vertical"></LinearLayout>
</android.support.v4.widget.DrawerLayout>

写好布局文件后,我们再来看看activity是怎么实现这个效果的, 请看下面代码:

    ...
    private ActionBarDrawerToggle mDrawerToggle;
    @InjectView(R.id.drawer)
    DrawerLayout drawerLayout;
    ...
    //设置侧边栏
    private void setDrawerLayout() {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        mDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);//和Toolbar也绑定
        mDrawerToggle.syncState();
        drawerLayout.setDrawerListener(mDrawerToggle);
    }

ToolBar

ToolBar是android5.0推出的一个新控件,用来替换ActionBar,说是更加灵活。如何使用?
首先,要设置这个activity的主题为不包含actionBar.

//no action bar
<style name="AppToolBarTheme" parent="Theme.AppCompat.Light.NoActionBar">
</style>
//Toolbar布局文件如下,Toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/holo_blue_light"
    app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"
    app:theme="@style/AppBaseTheme">
</android.support.v7.widget.Toolbar>

接着,在Activity里面加入Toolbar相关代码。

@InjectView(R.id.toolbar)
Toolbar toolbar;
//设置ToolBar
private void setToolBar() {
    toolbar.setTitle(R.string.home_page);
    toolbar.setNavigationIcon(R.mipmap.ic_drawer_am);
    setSupportActionBar(toolbar);//设置Toolbar
}
//设置ToolBar的相关选项
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.toolbar_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem menu) {
    switch (menu.getItemId()) {
        case R.id.action_login:
            break;
        case R.id.action_night:
            break;
        case R.id.action_settiong:
            break;
        default:
            break;
    }
    return true;
}

ToolBar里面的选项,是加载res/menu/toolbar_menu.xml这个布局文件:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".ContentActivity">
    <item
        android:id="@+id/action_login"
        android:icon="@mipmap/ic_drawer_am"
        android:orderInCategory="100"
        android:title="Login"
        app:showAsAction="always" />
    <item
        android:id="@+id/action_settiong"
        android:orderInCategory="100"
        android:title="action_settings"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_night"
        android:orderInCategory="100"
        android:title="Night"
        app:showAsAction="never"></item>
</menu>

SwipeRefreshLayout下拉刷新

下拉刷新只需要在布局文件的最外层包裹他就可以了,布局文件请看前面activity_content.xml的布局文件。在ContentActivity去实现SwipeRefreshLayout.OnRefreshListener。请看代码:

//设置下拉刷新
private void setSwipeLayout() {
    swipeRefreshLayout.setOnRefreshListener(this);
    swipeRefreshLayout.setColorSchemeResources(R.color.blue);
}
//下拉刷新监听
@Override
public void onRefresh() {
    BaseApplication.getHandler().postDelayed(new Runnable() {
        @Override
        public void run() {
            swipeRefreshLayout.setRefreshing(false);//刷新完毕
            Toast.makeText(BaseApplication.getApplication(), "刷新成功", Toast.LENGTH_SHORT).show();
        }
    }, 3000);
}

RecylerView列表控件

RecylerView也是V7包下的控件,我们简单理解为ListView控件的升级版就行。它的使用,无非就是
1. 设置布局管理器。
2. 设置适配器。
3. 设置Item相关ItemAnimator,增删动画。
在ContentActivity里面数据加载后,设置RecylerView。

//初始化数据相关
private void initData() {
    homeProtocol = new HomeProtocol();
    new Thread(new Runnable() {
        @Override
        public void run() {
            homeInfo = homeProtocol.loadData();
            BaseApplication.getHandler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    setRecylerView();
                }
            }, 500);
        }
    }).start();
}
//设置recyler view
private void setRecylerView() {
    HomeRecylerViewAdapter adapter = new HomeRecylerViewAdapter(homeInfo);//适配器
    LinearLayoutManager layoutManager = new LinearLayoutManager(BaseApplication.getApplication());//布局信息,设置Item显示的样式
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    recylerView.setLayoutManager(layoutManager);//设置LayoutManager
    recylerView.setAdapter(adapter);//设置适配器
}

HomeRecylerViewAdapter主要就三个方法
1. getItemCount
2. onCreateViewHolder
3. onBindViewHolder

package com.example.seaice.zhihuribao.adapter;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.seaice.zhihuribao.R;
import com.example.seaice.zhihuribao.Utils.BaseApplication;
import com.example.seaice.zhihuribao.bean.HomeInfo;
import com.example.seaice.zhihuribao.bean.HomeStoriesInfo;
import com.example.seaice.zhihuribao.bean.HomeTopInfo;
import com.example.seaice.zhihuribao.view.CircleIndicator;
import com.squareup.picasso.Picasso;
import java.util.List;
import butterknife.ButterKnife;
/**
 * Created by seaice on 2016/8/22.
 */
public class HomeRecylerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_HEADER = 0;
    private static final int TYPE_ITEM = 1;
    private static final int TYPE_DATE = 2;
    private LayoutInflater mInflate;
    private Context mContext;
    private List<HomeStoriesInfo> homeStoriesInfos;
    private List<HomeTopInfo> homeTopInfos;
    private HomeInfo homeInfo;
    private boolean isTouchViewPager = false;
    private boolean isViewPagerLoop = false;

    public HomeRecylerViewAdapter(HomeInfo homeInfo) {
        this.homeInfo = homeInfo;
        initData();
    }

    private void initData() {
        homeStoriesInfos = homeInfo.getHomeStoriesInfos();
        homeTopInfos = homeInfo.getHomeTopInfoList();
        mContext = BaseApplication.getApplication();
        mInflate = LayoutInflater.from(mContext);
    }

    //绑定不同类型Holder
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER) {
            View view = mInflate.inflate(R.layout.header_recyler_view, parent, false);
            return new HeaderHolder(view);
        } else if (viewType == TYPE_DATE) {
            View view = mInflate.inflate(R.layout.date_recyler_view, parent, false);
            return new DateHolder(view);
        } else if (viewType == TYPE_ITEM) {
            View view = mInflate.inflate(R.layout.item_recyler_view, parent, false);
            return new MyViewHolder(view);
        }
        throw new RuntimeException("there is no type that match the type " + viewType);
    }

    //设置holder的view元素
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder vh, int position) {
        if (vh instanceof HeaderHolder) {
            if(isViewPagerLoop == false){
                isViewPagerLoop = true;
                setViewPager((HeaderHolder) vh);
            }
        } else if (vh instanceof DateHolder) {
        } else if (vh instanceof MyViewHolder) {
            HomeStoriesInfo info = homeStoriesInfos.get(position - 2);
            MyViewHolder holder = (MyViewHolder) vh;
            holder.tv_title.setText(info.getTitle());
            Picasso.with(BaseApplication.getApplication()).load(info.getImages()).into(holder.iv_title);
        } else {
            throw new RuntimeException("there is no holer that match the  " + vh);
        }
    }

    @Override
    public int getItemCount() {
        return homeStoriesInfos.size() + 1;
    }

    @Override
    public int getItemViewType(int position) {
        if (isPositionHeader(position)) {
            return TYPE_HEADER;
        } else if (isPositionDate(position)) {
            return TYPE_DATE;
        } else {
            return TYPE_ITEM;
        }
    }

    private boolean isPositionDate(int position) {
        return position == 1;
    }

    private boolean isPositionHeader(int position) {
        return position == 0;
    }

    //设置viewPager
    private void setViewPager(final HeaderHolder holder) {
        holder.viewPager.setAdapter(new HomeViewPagerAdapter(homeTopInfos));
        holder.indicator.setCircleNumber(homeTopInfos.size());
        holder.viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageSelected(int position) {
                holder.indicator.setSelectdItem(position);
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                if (state == ViewPager.SCROLL_STATE_DRAGGING) {
                    isTouchViewPager = true;
                } else {
                    isTouchViewPager = false;
                }
            }
        });
        loopViewPager(holder);
    }

    //让viewpager循环播放
    private void loopViewPager(final HeaderHolder holder) {
        BaseApplication.getHandler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (isTouchViewPager == false) {
                    int item = (holder.viewPager.getCurrentItem()) + 1;
                    holder.viewPager.setCurrentItem(item % (holder.viewPager.getAdapter().getCount()));
                    holder.indicator.setSelectdItem(item);
                }
                loopViewPager(holder);
            }
        }, 5000);
    }

    //item view
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public ImageView iv_title;
        public TextView tv_title;

        public MyViewHolder(View view) {
            super(view);
            iv_title = ButterKnife.findById(view, R.id.iv_title);
            tv_title = ButterKnife.findById(view, R.id.tv_title);
        }
    }

    //header view
    public class HeaderHolder extends RecyclerView.ViewHolder {
        public ViewPager viewPager;
        public CircleIndicator indicator;

        public HeaderHolder(View view) {
            super(view);
            viewPager = ButterKnife.findById(view, R.id.viewPager);
            indicator = ButterKnife.findById(view, R.id.indicator);
        }
    }

    //dateHolder view
    public class DateHolder extends RecyclerView.ViewHolder {
        public TextView tv_date;
        public DateHolder(View view) {
            super(view);
            tv_date = ButterKnife.findById(view, R.id.tv_date);
        }
    }
}

HomeProtocol加载数据

HomeProtocol的逻辑实际上和GuideProtocol逻辑是一样的,那么此刻可以提取出一个基类,然后子类去实现各自的方法便可以。
基类,提取为BaseProtocol,后面子类只需要各自实现三个抽象方法便可以。
1. paserJsonData(String json) …//解析json数据
2. getCacheDir()….//获取缓存的文件路劲
3. getUrl()…//获取网页地址

package com.example.seaice.zhihuribao.protocol;

import android.util.Log;
import com.example.seaice.zhihuribao.Utils.FileUtils;
import com.example.seaice.zhihuribao.Utils.HttpUtils;
import com.example.seaice.zhihuribao.Utils.LogUtils;
import com.example.seaice.zhihuribao.bean.GuideInfo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by seaice on 2016/8/18.
 */
public abstract class BaseProtocol<T> {

    //解析json数据
    protected abstract T paserJsonData(String json);
    //获取连接路径
    protected abstract String getCacheDir();
    //获取新闻链接网址
    protected abstract String getUrl();

    /**
     * 1.先从本地获取json数据
     * 2.如果为空,再从服务器上获取
     * 3.若获取到json,保存到本地
     * 4.获取到json不为空,解析json数据
     */
    public T loadData() {
        String json = loadDataFromLocal();
        if (json == null) {
            LogUtils.e("从服务器获取json");
            json = loadDataFromServer();
            if (json != null) {
                saveDataToLocal(json);
            }
        } else {
            LogUtils.e("已经从本地获取");
        }
        if (json != null) {
            return paserJsonData(json);
        }
        return null;
    }

    //1.把整个json文件写到一个本地文件中
    //2.把每条数据摘出来保存到数据库中
    private String loadDataFromLocal() {
        File dir = FileUtils.getCacheDir();
        File file = new File(dir, getCacheDir());

        try {
            FileReader fr = new FileReader(file);
            BufferedReader br = new BufferedReader(fr);
            long saveTime = Long.parseLong(br.readLine());
            if (System.currentTimeMillis() > saveTime) {
                return null;
            } else {
                String str;
                StringWriter sw = new StringWriter();
                while ((str = br.readLine()) != null) {
                    sw.write(str);
                }
                LogUtils.i("本地获取数据");
                return sw.toString();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    //把数据保存到本地
    private void saveDataToLocal(String json) {
        LogUtils.i("保存到本地");
        //第一行写时间,然后保存起来
        File dir = FileUtils.getCacheDir();
        File file = new File(dir, getCacheDir());
        BufferedWriter bw = null;
        try {
            FileWriter fw = new FileWriter(file);
            bw = new BufferedWriter(fw);
            bw.write(System.currentTimeMillis() + 1000 * 100 + "");
            bw.newLine();
            bw.write(json);
            bw.flush();
            fw.close();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //从服务器加载数据
    private String loadDataFromServer() {
        String result = null;
        Request request = new Request.Builder().url(getUrl()).build();
        Response response = null;
        response = HttpUtils.execute(request);
        if (response != null && response.isSuccessful()) {
            try {
                result = response.body().string();
                LogUtils.e(result);
                return result;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

好了,那么剩下的就交给子类HomeProtocol.java来实现

package com.example.seaice.zhihuribao.protocol;

import com.example.seaice.zhihuribao.Utils.LogUtils;
import com.example.seaice.zhihuribao.bean.HomeInfo;
import com.example.seaice.zhihuribao.bean.HomeStoriesInfo;
import com.example.seaice.zhihuribao.bean.HomeTopInfo;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by seaice on 2016/8/18.
 */
public class HomeProtocol extends BaseProtocol<HomeInfo> {

    private static final String HOMEURL = "http://news-at.zhihu.com/api/4/news/latest";
    private static final String HOMECACHE = "home";

    @Override
    protected HomeInfo paserJsonData(String json) {
        try {
            JSONObject jsonObject = new JSONObject(json);
            String date = jsonObject.getString("date");
            JSONArray jsonArrayStories = jsonObject.getJSONArray("stories");
            List<HomeStoriesInfo> homeStoriesInfos = new ArrayList<HomeStoriesInfo>();
            for (int i = 0; i < jsonArrayStories.length(); i++) {
                JSONObject object = jsonArrayStories.getJSONObject(i);
                int type = object.getInt("type");
                String id = object.getString("id");
                String ga_prefix = object.getString("ga_prefix");
                String title = object.getString("title");
                String images = object.getString("images");
                String image = images.substring(2, images.length()-2);
                image = image.replace("\\", "");//去掉这个斜杠才能得到数据,take me a lot of time
                HomeStoriesInfo homeStoriesInfo = new HomeStoriesInfo(image, type,id,ga_prefix,title);
                LogUtils.e(homeStoriesInfo.toString());
                homeStoriesInfos.add(homeStoriesInfo);
            }
            JSONArray jsonArrayTop = jsonObject.getJSONArray("top_stories");
            List<HomeTopInfo> homeTopInfoList = new ArrayList<HomeTopInfo>();
            for (int i = 0; i < jsonArrayTop.length(); i++) {
                JSONObject object = jsonArrayTop.getJSONObject(i);
                String image = object.getString("image");
                int type = object.getInt("type");
                String id = object.getString("id");
                String ga_prefix = object.getString("ga_prefix");
                String title = object.getString("title");
                HomeTopInfo homeTopInfo = new HomeTopInfo(image,type,id,ga_prefix,title);
                homeTopInfoList.add(homeTopInfo);
            }
            HomeInfo homeInfo = new HomeInfo(date, homeStoriesInfos,homeTopInfoList);
            return homeInfo;
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected String getCacheDir() {
        return HOMECACHE;
    }

    @Override
    protected String getUrl() {
        return HOMEURL;
    }
}

ViewPager主页面第一条Item

RecylerView的第一条Item是一个ViewPager,不是其他普通的目录。怎么区分对待这种不同的Item呢?我的做法就是用不同的viewHolder,设置不同的type,让adapter区分处理。详细见上面的HomeRecylerViewAdapter。其他Item用CardView来实现,布局文件item_revyler_view.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:contentPadding="5dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cardCornerRadius="5dp"
    app:cardElevation="1dp"
    app:cardPreventCornerOverlap="true"
    app:cardUseCompatPadding="true"
    android:layout_marginBottom="2dp"
    android:layout_marginRight="5dp"
    android:layout_marginLeft="5dp">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/iv_title"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_alignParentRight="true"
            android:layout_margin="5dp" />

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_margin="5dp"
            android:layout_toLeftOf="@id/iv_title"
            android:gravity="center_vertical"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            android:ellipsize="end"/>
    </RelativeLayout>
</android.support.v7.widget.CardView>

再来说说viewPager,他是V4包里一个被用到烂的控件,实现左右滑动的效果。下面的圆点作为indicator指示滑动到哪个界面,自定义一个layout来画底部的圆点。
看看viewpager的布局文件, header_recyler_view.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_vp"
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="200dp"></android.support.v4.view.ViewPager>

    <com.example.seaice.zhihuribao.view.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/viewPager"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="8dp" />
</RelativeLayout>

其中CircleIndicator是底部的圆点,自定义view:

package com.example.seaice.zhihuribao.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import com.example.seaice.zhihuribao.R;

public class CircleIndicator extends View {

    private int mCount;//设置格数
    private int selectItem;//当前选中
    private float mRadius;//原点半径
    private float mSelectRadius;//选中原点半径
    private float mSpace;//圆形间隔
    private Paint mPaint;//画笔


    public CircleIndicator(Context context) {
        this(context, null);
    }

    public CircleIndicator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        setSize();
    }

    //设置圆点的大小
    private void setSize() {
        DisplayMetrics metrcs = getResources().getDisplayMetrics();
        mRadius = 2 * metrcs.density;
        mSelectRadius = 3 * metrcs.density;
        mSpace = 12 * metrcs.density;
    }

    //初始化画笔
    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(getResources().getColor(R.color.light_grey));
        mPaint.setStyle(Paint.Style.FILL);
    }

    public void setViewPager(ViewPager pager) {
        mCount = pager.getAdapter().getCount();
        invalidate();
    }

    //设置圆点的个数
    public void setCircleNumber(int num){
        mCount = num;
        invalidate();
    }

    //设置选中的圆点
    public void setSelectdItem(int position) {
        selectItem = position;
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int widthMeasureSpec) {
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
        int result;
        if (specMode != MeasureSpec.EXACTLY) {
            result = (int) (getPaddingLeft() + getPaddingRight() + mSpace * mCount);
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(specSize, result);
            }
        } else {
            result = specSize;
        }
        return result;
    }

    private int measureHeight(int heightMeasureSpec) {
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);
        int result;
        if (specMode != MeasureSpec.EXACTLY) {
            result = (int) (getPaddingTop() + getPaddingBottom() + mSelectRadius * 2);
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(specSize, result);
            }
        } else {
            result = specSize;
        }
        return result;
    }

    //画圆点
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float y = getHeight() / 2f;
        float x = mSpace / 2f;
        for (int i = 0; i < mCount; i++) {
            if (i != selectItem) {
                canvas.drawCircle(x, y, mRadius, mPaint);
            } else {
                canvas.drawCircle(x, y, mSelectRadius, mPaint);
            }
            x += mSpace;
        }
    }
}

至此,主页面的效果基本是做出来了,基本上用到的控件也都说清楚了。那么接下来的问题是:

  • RecylerView 如何上拉加载更多条目?

这个问题,留到下一篇解决,现在我也不知道怎么弄,嘿嘿。
代码我都上传到GitHub了,公司上传不了,只能每次拷贝回家上传代码。地址是:

https://github.com/jwsn/zhihuribao2

欢迎查阅!!

基于STM32F407,使用DFS算法实现最短迷宫路径检索,分为三种模式:1.DEBUG模式,2. 训练模式,3. 程序模式 ,DEBUG模式要分析bug,测量必要数据,训练模式用于DFS算法训练最短路径,并将最短路径以链表形式存储Flash, 程序模式从Flash中….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值