MEIZHI gankio 程序 一
来源:互联网 发布:operamasks ui.min.js 编辑:程序博客网 时间:2024/06/05 08:52
项目地址:https://github.com/drakeet/Meizhi
整个项目的封装思想很值得学习。之前写过的一篇文章
一、
再倒入包的时候 看到他的build.gradle 许多奇怪的配置
原来是使用了config.gradle统一管理项目,先新建一个config.gradle的文件
ext {
android = [compileSdkVersion: 23,
buildToolsVersion: "23.0.3",
applicationId : "me.drakeet.meizhi",
minSdkVersion : 16,
targetSdkVersion : 22,
versionCode : 360,
versionName : "2.6.0"]
dependencies = ["appcompat-v7" : "com.android.support:appcompat-v7:23.4.0",
"design" : "com.android.support:design:23.4.0",
"recyclerview-v7" : "com.android.support:recyclerview-v7:23.4.0",
"nineoldandroids" : "com.nineoldandroids:library:2.4.0",
"picasso" : "com.squareup.picasso:picasso:2.5.2",
"photoview" : "com.github.chrisbanes.photoview:library:1.2.3",
"numberprogressbar" : "com.daimajia.numberprogressbar:library:1.2@aar",
"umeng-analytics" : "com.umeng.analytics:analytics:latest.integration",
"retrofit" : "com.squareup.retrofit:retrofit:1.9.0",
"rxandroid" : "io.reactivex:rxandroid:1.0.0",
"okhttp-urlconnection": "com.squareup.okhttp:okhttp-urlconnection:2.0.0",
"okhttp" : "com.squareup.okhttp:okhttp:2.0.0",
"butterknife" : "com.jakewharton:butterknife:7.0.1",
"otto" : "com.squareup:otto:1.3.8",
"glide" : "com.github.bumptech.glide:glide:3.7.0"]
}
然后在项目的build.gradle文件中引入
apply from: "config.gradle"
这样使用config中的配置就可以管理所有module中的配置了.
如何使用
在module的build.gradle中使用:
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
applicationId rootProject.ext.android.applicationId
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
}
compile rootProject.ext.dependencies["design"]
compile rootProject.ext.dependencies["appcompat-v7"]
compile rootProject.ext.dependencies["recyclerview-v7"]
compile rootProject.ext.dependencies["picasso"]
二、
从Mainactivity来看,其获取layout和findviewbyid使用到了Butterknife库,然后学习了使用最新版本8.0.4.
减少代码量,8.0不同于其他,需要一些不同的配置才能生效,否则报获取view空
如何改变:
Project的build.gradle文件中:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //8.X以上需要多配置的代码
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app的build.gradle文件中内容:
apply plugin: 'com.neenbedankt.android-apt'//8.X以上需要多配置的代码
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.jakewharton:butterknife:8.1.0'
apt 'com.jakewharton:butterknife-compiler:8.1.0'//8.X以上需要多配置的代码
}
代码中的使用,具体就是之前的inject变成了现在的bind,其他变化不大:
(与最早之前的相比。bind变为了bindview 并且有了bindviews。)
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn_qq) Button qqBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btn_qq) void qqBtnClick(){
Toast.makeText(MainActivity.this, "123", Toast.LENGTH_SHORT).show();
}
}
这些在官网文档也是有的 (https://github.com/JakeWharton/butterknife);
三、
然后他的主界面是一个Recycleview加载从gank.io接口中传来的图片和内容,他建立了一个bean实体类,也是用到了创建表格,存在了表格
里边,创建表用到的不是自带的sqlite数据库,用到的是liteorm第三方库,性能比自带的好一点。
android-lite-orm库的github地址:
https://github.com/litesuits/android-lite-orm
新建表:
import com.litesuits.orm.db.annotation.Column;
import com.litesuits.orm.db.annotation.Table;
import java.util.Date;
/**
* Created by drakeet on 6/20/15.
*/
@Table("meizhis") public class Meizhi extends Soul {
@Column("url") public String url;
@Column("type") public String type;
@Column("desc") public String desc;
@Column("who") public String who;
@Column("used") public boolean used;
@Column("createdAt") public Date createdAt;
@Column("updatedAt") public Date updatedAt;
@Column("publishedAt") public Date publishedAt;
@Column("imageWidth") public int imageWidth;
@Column("imageHeight") public int imageHeight;
}
@Table @Column 为标签.看源码就会很清楚. 就是用的建表语句建表语句:CREATE TABLE IF NOT EXISTS test_model (id INTEGER PRIMARY KEY AUTOINCREMENT ,name TEXT, login TEXT DEFAULT true)
四、
然后是调用接口 gason结束数据到实体中,然后图片的加载是用到的picasso
在加载图片这块写的很好,可以学习如何activity带动画的跳转到另一个activity,并且用picasso预加载将图片加载到Imageview中,然后长按
图片弹出对话框,可以保存到指定目录,保存的代码块可以学习,
实现代码
mMeizhiListAdapter.setOnMeizhiTouchListener(getOnMeizhiTouchListener()); //添加点击事件
//区分点击妹子图片和点击itemView
private OnMeizhiTouchListener getOnMeizhiTouchListener() {
return (v, meizhiView, card, meizhi) -> {
if (meizhi == null) return;
if (v == meizhiView && !mMeizhiBeTouched) { //使用标志位mMeizhiBeTouched过滤重复点击
mMeizhiBeTouched = true;
//利用Picasso预加载图片
Picasso.with(this).load(meizhi.url).fetch(new Callback() {
@Override public void onSuccess() { //如果预加载成功就跳转展示图片
mMeizhiBeTouched = false;
startPictureActivity(meizhi, meizhiView);
}
@Override public void onError() {mMeizhiBeTouched = false;}
});
} else if (v == card) { //如果是item被点击
startGankActivity(meizhi.publishedAt);
}
};
}
查看MeizhiListAdapter的相关代码
找到调用mOnMeizhiTouchListener的地方.
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@Bind(R.id.iv_meizhi) RatioImageView meizhiView; //显示妹子图片的View
@Bind(R.id.tv_title) TextView titleView;
View card; //即Item
Meizhi meizhi; //封装的妹子图片对象
public ViewHolder(View itemView) {
super(itemView);
card = itemView;
ButterKnife.bind(this, itemView);
meizhiView.setOnClickListener(this); //妹子图片设置点击事件
card.setOnClickListener(this); //itemView设置点击事件
meizhiView.setOriginalSize(50, 50);
}
@Override public void onClick(View v) {
mOnMeizhiTouchListener.onTouch(v, meizhiView, card, meizhi);
}
}
这里让ViewHolder实现点击事件. 让itemView和妹子图片的点击共用一个监听器.
这样做的好处就是方便,简单.
但是ViewHolder以组合方式持有了MeiZhi这个对象的引用,不晓得恰不恰当.
另外,代码中使用了Picasso的预加载功能, 如果能成功加载就打开图片浏览页面,不能则放弃打开. 这样的体验也不错.
转场动画
开启图片浏览的Activity这里有个转场动画.使用的是ActivityOptionsCompat.makeSceneTransitionAnimation(),
平滑的将一个控件平移的过渡到第二个activity.
private void startPictureActivity(Meizhi meizhi, View transitView) {
Intent intent = PictureActivity.newIntent(MainActivity.this, meizhi.url, meizhi.desc); //这种在PictureActivity中写newIntent的方式并推荐
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
MainActivity.this, transitView, PictureActivity.TRANSIT_PIC); //转场动画,PictureActivity.TRANSIT_PIC这个name很重要
try {
ActivityCompat.startActivity(MainActivity.this, intent, optionsCompat.toBundle());
} catch (IllegalArgumentException e) {
e.printStackTrace();
startActivity(intent);
}
}
在PictureAcitity的onCreate()中调用了
ViewCompat.setTransitionName(mImageView, TRANSIT_PIC);
要使用这个方法就必须给两个不同Activity的中的布局元素设定同样的一个android:transitionName,然后还需要一个标志来告诉Window执行动画,因为这个只是在5.x上有效.
此部分内容来自:http://www.voidcn.com/blog/maxwell0401/article/p-6145720.html
五、RecycleView的滑动,一瀑布流形式显示图片和内容描述, 监听图片滚动,加载下一页的处理。
绑定监听器
final StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,
StaggeredGridLayoutManager.VERTICAL); //瀑布流布局管理器
mRecyclerView.setLayoutManager(layoutManager);
mMeizhiListAdapter = new MeizhiListAdapter(this, mMeizhiList);
mRecyclerView.setAdapter(mMeizhiListAdapter);
/**
* 第一次登陆的话 show的内容Snackbar展示
*/
new Once(this).show("tip_guide_6", () -> {
Snackbar.make(mRecyclerView, getString(R.string.tip_guide), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.i_know, v -> {//点击的按钮
})
.show();
});
mRecyclerView.addOnScrollListener(getOnBottomListener(layoutManager)); //添加滚动监听
监听器的实现
//滚动到底部自动加载下一页
RecyclerView.OnScrollListener getOnBottomListener(StaggeredGridLayoutManager layoutManager) {
return new RecyclerView.OnScrollListener() {
@Override public void onScrolled(RecyclerView rv, int dx, int dy) {
//是否到达底部, 因为是2列,所以参数是new int[2], 判断第二列显示的View的数量是否显示完全
boolean isBottom =layoutManager.findLastCompletelyVisibleItemPositions(new int[2])[1] >=
mMeizhiListAdapter.getItemCount() - PRELOAD_SIZE;
if (!mSwipeRefreshLayout.isRefreshing() && isBottom) {
if (!mIsFirstTimeTouchBottom) {
mSwipeRefreshLayout.setRefreshing(true); //显示刷新
mPage += 1; //加载下一页
loadData();
} else {
mIsFirstTimeTouchBottom = false;
}
}
}
};
}
API说明
StaggeredGridLayoutManager#findLastCompletelyVisibleItemPositions()的API说明:
/** * Returns the adapter position of the last completely visible view for each span. * <p> 返回每一列可见的最后一个view的位置的数组 * @param into An array to put the results into. If you don't provide any, LayoutManager will * create a new one. 用来存放结果的数组into[] * @return The adapter position of the last fully visible item in each span. If a span does not * have any items, {@link RecyclerView#NO_POSITION} is returned for that span. */
public int[] findLastCompletelyVisibleItemPositions(int[] into) {
if (into == null) {
into = new int[mSpanCount]; //根据设置的spanCount来创建数组
} else if (into.length < mSpanCount) {
throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
+ " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
}
for (int i = 0; i < mSpanCount; i++) {
into[i] = mSpans[i].findLastCompletelyVisibleItemPosition(); //每个span都是找到最后一个位置
}
return into;
}
六、点击图片 预加载成功后的长按事件弹出对话框,保存图片到本地文件夹,然后会更新图片到相册。
有Mainactivit跳转到PictureActivity.在此进行处理 跳转期间还有动画效果同事进行图片的加载
/**
* 保存图片到指定路径
*/
private void saveImageToGallery() {
// @formatter:off
Subscription s = RxMeizhi.saveImageAndGetPathObservable(this, mImageUrl, mImageTitle)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(uri -> {
File appDir = new File(Environment.getExternalStorageDirectory(), "Meizhi");
String msg = String.format(getString(R.string.picture_has_save_to),
appDir.getAbsolutePath());
Toasts.showShort(msg);
}, error -> Toasts.showLong(error.getMessage() + "\n再试试..."));
// @formatter:on
addSubscription(s);
}
/**
* 长按图片点击事项
*/
private void setupPhotoAttacher() {
mPhotoViewAttacher = new PhotoViewAttacher(mImageView);
mPhotoViewAttacher.setOnViewTapListener((view, v, v1) -> hideOrShowToolbar());
// @formatter:off
mPhotoViewAttacher.setOnLongClickListener(v -> {
new AlertDialog.Builder(PictureActivity.this)
.setMessage(getString(R.string.ask_saving_picture))
.setNegativeButton(android.R.string.cancel,
(dialog, which) -> dialog.dismiss())
.setPositiveButton(android.R.string.ok,
(dialog, which) -> {
saveImageToGallery();
dialog.dismiss();
})
.show();
// @formatter:on
return true;
});
}
关键处理类RxMeizhi.java 包括避免重复插入,进行预加载是否成功的判断, 以及进行图库的更行
/**
* 简单重构了下,并且修复了重复插入图片问题
* Created by drakeet on 8/10/15.
*/
public class RxMeizhi {
public static Observable<Uri> saveImageAndGetPathObservable(Context context, String url, String title) {
return Observable.create(new Observable.OnSubscribe<Bitmap>() {
@Override public void call(Subscriber<? super Bitmap> subscriber) {
Bitmap bitmap = null;
try {
bitmap = Picasso.with(context).load(url).get();
} catch (IOException e) {
subscriber.onError(e);
}
if (bitmap == null) {
subscriber.onError(new Exception("无法下载到图片"));
}
subscriber.onNext(bitmap);
subscriber.onCompleted();
}
}).flatMap(bitmap -> {
File appDir = new File(Environment.getExternalStorageDirectory(), "Meizhi");
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = title.replace('/', '-') + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream outputStream = new FileOutputStream(file);
assert bitmap != null;
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Uri uri = Uri.fromFile(file);
// 通知图库更新
Intent scannerIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
context.sendBroadcast(scannerIntent);
return Observable.just(uri);
}).subscribeOn(Schedulers.io());
}
}
- MEIZHI gankio 程序 一
- 开源项目Meizhi学习笔记--RatioImageView
- 开源项目Meizhi学习笔记
- 开源项目Meizhi学习笔记--VideoImageView
- 开源项目MeiZhi源码阅读
- 开源项目Meizhi学习笔记--工具类的收集
- 开源项目Meizhi学习笔记--保存图片到本地
- 程序一
- 程序一
- 开源项目GankIo 仿写(MVP+Retrofit+butterKnife + MaterialDesign)
- 带你学开源项目:Meizhi Android之RxJava & Retrofit最佳实践
- 开源项目解析:Meizhi Android之RxJava & Retrofit最佳实践
- 程序人生(一)
- arx程序说明(一)
- 入门一程序实例
- socket 程序编写(一)
- TIPTOPGP 公用程序(一)
- 程序架构(一)
- MSSQLSERVER服务 请求失败或服务未及时响应。有关详细信息请参见事件日志或其他的异常日志
- Android系统篇之----Binder机制和远程服务调用
- NHibernate与EF(Entity Framework)的区别
- 在Yarn上运行spark-shell和spark-sql命令行
- 没有一部是烂片 香港最强水准的10部影片
- MEIZHI gankio 程序 一
- 根据生日计算年龄
- SQL Server 2012 Express LocalDB
- 解决MySQl卸载卸不干净问题
- GPS知识
- 求孪生素数的对数
- [hystar整理]Entity Framework 教程
- 网络库小常识------持续编辑ing
- MongoDB入门操作