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

    xiaoxiao2026-08-20  8

    转载请注明出处: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); } } }
    转载请注明原文地址: https://ju.6miu.com/read-1311366.html
    最新回复(0)