高仿知乎日报(一)[引导页面]

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

引导界面

一直想写一个与网络有交互的app来练练手,偶然发现知乎日报这个app,想着就着手模仿一个,尽管已经有人完全写了出来,但是自己不妨也写一个,全当练习。
这一篇就来写写引导页面,就是一张逐步放大的图片外加几个文本组成。效果图是这样(不会弄gif动态图):

这里写图片描述

布局文件

首先,先搭好UI界面,很简单,两个TextView+一个ImageView, 布局文件如下:

<?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">

    <ImageView
        android:id="@+id/iv_guide"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY" />

    <TextView
        android:id="@+id/tv_guide"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="15dp"
        android:textColor="@android:color/darker_gray"
        android:textSize="15sp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/tv_guide"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:text="@string/app_name"
        android:textColor="@android:color/white"
        android:textSize="28sp" />
</RelativeLayout>

其次,ImageView渐变放大效果的实现,用Animation。配好Animation 的xml文件,
在目录res/anim/guide_imageview_zoom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">//表示放大后效果不重置
    <scale
        android:duration="2000"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.2"
        android:toYScale="1.2" />
</set>

Activity主要逻辑

Activity的主体逻辑,是从服务器获取资源,设置给布局上的控件。来看看这个activity的逻辑代码。

package com.example.seaice.zhihuribao;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.seaice.zhihuribao.Utils.BaseApplication;
import com.example.seaice.zhihuribao.Utils.ThreadMgr;
import com.example.seaice.zhihuribao.Utils.UiUtils;
import com.example.seaice.zhihuribao.bean.GuideInfo;
import com.example.seaice.zhihuribao.protocol.GuideProtocol;
import com.squareup.picasso.Picasso;

import butterknife.ButterKnife;
import butterknife.InjectView;

public class MainActivity extends AppCompatActivity {

    private GuideProtocol guideProtocol;//负责从网络和本地获取数据
    private GuideInfo guideInfo;

    @InjectView(R.id.iv_guide)
    ImageView iv_guide;

    @InjectView(R.id.tv_guide)
    TextView tv_guide;

    private Handler handler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.inject(this);
        guideProtocol = new GuideProtocol();
        setImageGuide();
    }

    //从服务器加载资源
    private void loadDataFromProtocol(final Runnable r){
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                guideInfo = guideProtocol.loadData();//获取数据
                UiUtils.runOnUiThread(r);
            }
        };
        ThreadMgr.getThreadPool().execute(runnable);
    }

    //设置imageview的放大效果
    private void setImageGuide(){
        Runnable r = new Runnable() {
            @Override
            public void run() {
                //guide image view
                      Picasso.with(BaseApplication.getApplication()).load(guideInfo.getGuidePic()).into(iv_guide);
                Animation guideImageZoomAni = AnimationUtils.loadAnimation(MainActivity.this, R.anim.guide_imageview_zoom);
                iv_guide.startAnimation(guideImageZoomAni);
                tv_guide.setText(guideInfo.getGuideName());
            }
        };
        loadDataFromProtocol(r);
    }
}

获取网络数据

GuideProtocol 是从网络/缓存获取json数据的一个封装类
1. 先从本地获取json数据
2. 如果为空,再从服务器上获取,并且保存到本地缓存
3. 若不为空,则直接解析json数据
以上便是这个类的主要逻辑,详细可以查看我之前的文章,三级缓存的基本原理
http://blog.csdn.net/amd123456789/article/details/52092927

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 org.json.JSONException;
import org.json.JSONObject;
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/11.
 */
public class GuideProtocol {
    private static final String GUIDE_URL = "http://news-at.zhihu.com/api/4/start-image/1080*1776";
    private static final String GUIDE_DIR = "guide";
    private static final String TAG = "GuideProtocol";

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

    private GuideInfo paserJsonData(String json) {
        try {
            JSONObject jsonObject = new JSONObject(json);
            String guideName = jsonObject.getString("text");
            String guidePicUrl = jsonObject.getString("img");
            GuideInfo guideInfo = new GuideInfo(guideName, guidePicUrl);
            Log.e(TAG, guideInfo.toString());
            return guideInfo;
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

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

        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, GUIDE_DIR);
        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(GUIDE_URL).build();
        Response response = null;
        response = HttpUtils.execute(request);
        if (response != null && response.isSuccessful()) {
            try {
                result = response.body().string();
                Log.e(TAG, result);
                return result;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

于此对应的GuideInfo为:

package com.example.seaice.zhihuribao.bean;
/**
 * Created by seaice on 2016/8/11.
 */
public class GuideInfo {

    public GuideInfo(String guideName, String guidePic) {
        this.guideName = guideName;
        this.guidePic = guidePic;
    }

    private String guideName;
    private String guidePic;

    public String getGuideName() {
        return guideName;
    }

    public void setGuideName(String guideName) {
        this.guideName = guideName;
    }

    public String getGuidePic() {
        return guidePic;
    }

    public void setGuidePic(String guidePic) {
        this.guidePic = guidePic;
    }

    @Override
    public String toString() {
        return "GuideInfo{" +
                "guideName='" + guideName + '\'' +
                ", guidePic=" + guidePic +
                '}';
    }
}

工具类介绍Utils

  • BaseApplication用来获取全局用的Context,主线程handler等,这个是在应用启动时便会跑,需要在清单文件中配置它。
package com.example.seaice.zhihuribao.Utils;

import android.app.Application;
import android.os.Handler;

/**
 * Created by seaice on 2016/8/11.
 */
public class BaseApplication extends Application {
    private static BaseApplication application;

    private static int mainThreadId;
    private static Handler handler;

    @Override
    public void onCreate(){
        super.onCreate();
        application = this;
        mainThreadId = android.os.Process.myTid();
        handler = new Handler();
    }

    public static BaseApplication getApplication(){
        return application;
    }

    public static int getMainThreadId(){
        return mainThreadId;
    }

    public static Handler getHandler(){
        return handler;
    }
}
  • FileUtils用来获取缓存文件的路径
package com.example.seaice.zhihuribao.Utils;

import android.os.Environment;
import android.util.Log;

import java.io.File;

/**
 * Created by seaice on 2016/8/11.
 */
public class FileUtils {
    private static final String TAG = "FileUtils";
    private static final String CACHE = "cache";
    private static final String ROOT = "zhihuribao";


    public static File getDir(String str){
        StringBuilder path = new StringBuilder();
        if(isSDAvailable()){
            path.append(Environment.getExternalStorageDirectory().getAbsolutePath());
            path.append(File.separator);
            path.append(ROOT);
            path.append(File.separator);
            path.append(str);
        }else{
            File fileDir = BaseApplication.getApplication().getCacheDir();
            path.append(fileDir.getAbsolutePath());
            path.append(File.separator);
            path.append(str);
        }
        Log.e(TAG, path.toString());
        File file = new File(path.toString());
        if(!file.exists() || !file.isDirectory()){
            file.mkdirs();
            LogUtils.i("make dirs");
        }
        return file;
    }

    public static File getCacheDir(){
        return getDir(CACHE);
    }

    private static boolean isSDAvailable() {
        return Environment.isExternalStorageEmulated();
    }
}
  • HttpUtils对网络相关操作的一个简单封装
package com.example.seaice.zhihuribao.Utils;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by seaice on 2016/8/11.
 */
public class HttpUtils {
    private static final String TAG = "HttpUtils";

    private static final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
            .readTimeout(5000, TimeUnit.MILLISECONDS).build();

    public static OkHttpClient getInstance() {
        return mOkHttpClient;
    }

    /**
     * 该不会开启异步线程。
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static Response execute(Request request) {
        try {
            return getInstance().newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 开启异步线程访问网络
     *
     * @param request
     * @param responseCallBack
     */
    public static void enqueue(Request request, Callback responseCallBack) {
        getInstance().newCall(request).enqueue(responseCallBack);
    }

    /**
     * 开启异步线程访问网络, 且不在意返回结果(实现空callback)
     *
     * @param request
     */
    public static Response enqueue(Request request) {
        mOkHttpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }

        });
        return null;
    }
}
  • LogUtils日志输出相关的工具类
package com.example.seaice.zhihuribao.Utils;

import android.util.Log;

/**
 * Created by seaice on 2016/8/12.
 */
public class LogUtils {
    private static final String TAG = "zhihuribao";

    public static final int LEVEL_NONE = 0;
    public static final int LEVEL_VERBOSE = 1;
    public static final int LEVEL_DEBUG = 2;
    public static final int LEVEL_INFO = 3;
    public static final int LEVEL_WARN = 4;
    public static final int LEVEL_ERROR = 5;

    private static int mDebugLevel = LEVEL_ERROR;

    public static void v(String msg) {
        if (mDebugLevel >= LEVEL_VERBOSE) {
            Log.v(TAG, msg);
        }
    }

    public static void d(String msg) {
        if (mDebugLevel >= LEVEL_DEBUG) {
            Log.d(TAG, msg);
        }
    }

    public static void i(String msg) {
        if (mDebugLevel >= LEVEL_INFO) {
            Log.i(TAG, msg);
        }
    }

    public static void w(String msg) {
        if (mDebugLevel >= LEVEL_WARN) {
            Log.w(TAG, msg);
        }
    }

    public static void e(String msg) {
        if (mDebugLevel >= LEVEL_ERROR) {
            Log.e(TAG, msg);
        }
    }
}
  • ThreadMgr线程池相关,管理线程的开启数量,减少开销
package com.example.seaice.zhihuribao.Utils;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by seaice on 2016/8/12.
 */
public class ThreadMgr {
    private static ThreadPool threadPool;

    public static ThreadPool getThreadPool(){
        if(threadPool == null){
            synchronized (ThreadMgr.class){
                if(threadPool == null){
                    int corePoolSize = 5;
                    int maxPoolSize = 10;
                    long keepAliveTime = 0L;
                    threadPool = new ThreadPool(corePoolSize,maxPoolSize,keepAliveTime);
                }
            }
        }
        return threadPool;
    }

    public static class ThreadPool{
        public static ThreadPoolExecutor executor = null;

        private int corePoolSize;
        private int maxPoolSize;
        private long keepAliveTime = 0;

        public ThreadPool(int corePoolSize, int maxPoolSize, long keepAliveTime){
            this.corePoolSize = corePoolSize;
            this.maxPoolSize = maxPoolSize;
            this.keepAliveTime = keepAliveTime;
        }

        public void execute(Runnable runnable){
            if(runnable == null){
                return;
            }

            if (executor == null) {
                /**
                 * 1.corePoolSize:里面可以开辟多少个线程
                 * 2.如果排队满了,额外的开的线程数
                 * 3.如果这个线程池没有要执行的任务,存活多久
                 * 4.时间单位
                 * 5.如果,线程池里管理的线程都已经用了,把剩下的任务都先临时存到这个对象中排队
                 */
                executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.MILLISECONDS,
                        new LinkedBlockingDeque<Runnable>(), Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());
            }
            executor.execute(runnable);
        }

        public void cancel(Runnable runnable){
            if(executor!=null && !executor.isShutdown() && !executor.isTerminated()){
                executor.remove(runnable);
            }
        }
    }
}
  • UiUtils和UI相关操作的工具类
package com.example.seaice.zhihuribao.Utils;

import android.content.res.Resources;
import android.util.Log;

/**
 * Created by seaice on 2016/8/12.
 */
public class UiUtils {
    private static final String TAG = "UiUtils";

    public static Resources getResource() {
        return BaseApplication.getApplication().getResources();
    }

    /**
     * 获取字符串数组
     *
     * @param tabNames
     * @return
     */
    public static String[] getStringArray(int tabNames) {
        return getResource().getStringArray(tabNames);
    }

    public static void runOnUiThread(Runnable runnable) {
        if (runnable == null) {
            return;
        }
        //证明在主线程
        if (android.os.Process.myTid() == BaseApplication.getMainThreadId()) {
            Log.e(TAG, "主线程");
            runnable.run();
        } else {
            Log.e(TAG, "非主线程");
            BaseApplication.getHandler().post(runnable);
        }
    }

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值