转载请注意:http://blog.youkuaiyun.com/wjzj000/article/details/52650058
本菜GitHub上开源了一个小的Android项目,感兴趣的看官大大们可以star下:
https://github.com/zhiaixinyang/MyFirstApp
这个项目是很久之前下载的,现在已经找不到相关链接...
今天重新再把它梳理一遍,并加以改动。即算是对一些知识点的巩固,也算是完成一次老师布置的作业。
ps:要求让合影,这就算了。影响心情...
首先看一下改动后版本和原项目的对比:
改动后
原版
首先项目结构如下,这个一个标准的MVP模式。
项目中包的创建是每个业务创建一个包。其下再通过MVP的模式进行解耦。
通过weather包下的内容来展开MVP模式:
接下来进入代码中逐步分析:
public interface WeatherModel {
//接口类,具体实现在WeatherModelImpl中
void loadWeatherData(String cityName, WeatherModelImpl.LoadWeatherListener listener);
void loadLocation(Context context, WeatherModelImpl.LoadLocationListener listener);
}
public class WeatherModelImpl implements WeatherModel {
private static final String TAG = "WeatherModelImpl";
//实现WeatherModel,复写相应的方法。
@Override
public void loadWeatherData(String cityName, final LoadWeatherListener listener) {
try {
String url = Urls.WEATHER + URLEncoder.encode(cityName, "utf-8");
//通过OKHttp进行数据请求。
OkHttpUtils.ResultCallback<String> callback = new OkHttpUtils.ResultCallback<String>() {
@Override
public void onSuccess(String response) {
List<WeatherBean> lists = WeatherJsonUtils.getWeatherInfo(response);
listener.onSuccess(lists);
}
@Override
public void onFailure(Exception e) {
listener.onFailure("load weather data failure.", e);
}
};
OkHttpUtils.get(url, callback);
} catch (UnsupportedEncodingException e) {
LogUtils.e(TAG, "url encode error.", e);
}
}
接下来进入presenter包下:
先看WeatherPresenterImpl中的内容,首先它必然要实现WeatherPresenter接口。值得注意的是他的构造方法。
public WeatherPresenterImpl(Context context, WeatherView weatherView) {
this.mContext = context;
this.mWeatherView = weatherView;
mWeatherModel = new WeatherModelImpl();
}
在这里需要传递一个WeatherView,这也是一个接口,通过名字可以看出它是一个和View有关的接口。没错,其实它就是Activity/Fragment的接口类。
那么在这里,我们可以细想,初始化的时候需要传递Activity/Fragment的接口,那么WeatherPresenterImpl势必要在Activity/Fragment中被初始化,事实也正是如此。当我们打开widget包下的WeatherFragment,我们可以看到:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWeatherPresenter = new WeatherPresenterImpl(getActivity().getApplication(), this);
}
在onCreate()方法中WeatherPresenterImpl被初始化。
关于MVP的分析就到此为止吧,其实思想比较简单就是抽象的时候需要好好琢磨一番...
以下是所用到的库
compile project(':swipeback')
compile project(':GuillotineMenu_library')//铡刀菜单库
compile project(':cardsUILib')//日常囧图中,卡片视图库
compile 'com.qiushui:blurredview:0.8.1'//动态模糊库
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.jakewharton:butterknife:8.0.1'
apt 'com.jakewharton:butterknife-compiler:8.0.1'
compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.android.support:design:23.4.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.squareup.okhttp:okhttp:2.7.0'
compile 'org.sufficientlysecure:html-textview:1.3'//超链接TextView库
compile 'com.android.support:cardview-v7:23.4.0'
compile 'com.zgh.collapsiblelistview:collapsiblelistview:1.0.1'//折叠式ListView
接下来谈一谈对铡刀菜单的改造。
首先铡刀菜单是国外一大神写的。效果是挺炫,不过华而不实吧。而且通过源码可以看出,作者主要是为了实现效果。拓展性并不强。使用它主要还是为了学习其中对动画的使用。通过内部封装的一个方法来窥探一下作业的思路。
private ObjectAnimator buildOpeningAnimation() {
ObjectAnimator rotationAnimator = initAnimator(ObjectAnimator.ofFloat(mGuillotineView,
ROTATION, GUILLOTINE_CLOSED_ANGLE, GUILLOTINE_OPENED_ANGLE));
rotationAnimator.setInterpolator(mInterpolator);
rotationAnimator.setDuration(mDuration);
rotationAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mGuillotineView.setVisibility(View.VISIBLE);
isOpening = true;
}
@Override
public void onAnimationEnd(Animator animation) {
isOpening = false;
if (mListener != null) {
mListener.onGuillotineOpened();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
return rotationAnimator;
}
可以看出来他使用的是属性动画,几个变量分别是rotation动画模式,以及旋转角度,并且设置动画的监听。已完成对动画的打开与关闭进行控制。不过我们可以看到这里用到了插值器也就是 Interpolator,被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。
public class GuillotineInterpolator implements TimeInterpolator { public static final float ROTATION_TIME = 0.46667f; public static final float FIRST_BOUNCE_TIME = 0.26666f; public static final float SECOND_BOUNCE_TIME = 0.26667f; public GuillotineInterpolator() { } public float getInterpolation(float t) { if (t < ROTATION_TIME) return rotation(t); else if (t < ROTATION_TIME + FIRST_BOUNCE_TIME) return firstBounce(t); else return secondBounce(t); } private float rotation(float t) { return 4.592f * t * t; } private float firstBounce(float t) { return 2.5f * t * t - 3f * t + 1.85556f; } private float secondBounce(float t) { return 0.625f * t * t - 1.083f * t + 1.458f; } }
isOpening=true是改造的内容,用于记录此时是打开状态还是关闭状态,方便对动画进行控制。
public void open() {
if (!isOpening) {
mOpeningAnimation.start();
}
}
public void close() {
if (!isClosing) {
mClosingAnimation.start();
}
}
关于铡刀菜单的使用:
guillotineAnimation = new GuillotineAnimation.GuillotineBuilder(guillotineMenu,
guillotineMenu.findViewById(R.id.guillotine_hamburger), contentHamburger)
.setStartDelay(RIPPLE_DURATION)
.setActionBarViewForAnimation(toolbar)
.setClosedOnStart(true)
.build();
从命名可以看出这是Builder设计模式。guillotioneMenu作为铡刀操作的View,contentHamburger是标题栏上的按钮,也就是用于操作铡刀菜单的开与关。
到这就先告一段落吧....