android图片查看(2)
2016-02-21 21:27
357 查看
上一篇文章里我们已经完成了图片查看的基本功能,接下来,我们要在这基础上增加一些新的功能。
Android support v7 Library里面有一个叫Palette的库,通过这个库,我们可以很方便的从图像中抽取颜色,我们看一下官方文档的介绍
A helper class to extract prominent colors from an image.
A number of colors with different profiles are extracted from the image:
1. Vibrant
2. Vibrant Dark
3. Vibrant Light
4. Muted
5. Muted Dark
6. Muted Light
These can be retrieved from the appropriate getter method.
我们这里不深究颜色是怎么提取的和每种颜色的区别,有兴趣的同学可以去查看官方文档。因为现在的背景是黑色的,所以我从图像中抽取Muted Dark这个颜色作为我们toolbar的背景。
Palette提供了四种方法生成对象
Palette generate(Bitmap bitmap)
Palette generate(Bitmap bitmap, int numColors)
generateAsync(Bitmap bitmap, PaletteAsyncListener listener)
generateAsync(Bitmap bitmap, int numColors, final PaletteAsyncListener listener)
前两种是同步方法,不过如果图片颜色比较丰富的话提取颜色的算法可能会比较耗时,所以,我们最好不要在主线程中使用。这里我们使用异步方法。
我们修改前面的加载图片方法
这样,我们的toolbar就能根据不同的图片变色了,有没有觉得很好玩呢:)。接下来,我们加点动画,让toolbar慢慢的从默认的颜色变化到指定的颜色,同时改变透明度。
这里涉及到颜色的改变,View Animation没有这个能力,我们使用Property Animation。
我们定义两个动画,因为这两个动画要一起动,所以再定义一个动画集合去控制
Palette取到颜色之后我们开始动画,所以继续修改上面的代码
这样,我们就可以实现在DURATION时间内将toolbar的颜色变化到Palette提取出来的颜色,并将toolbar的透明度变为0.5。
接下来,我们还要做什么呢?还是加动画:)。很多app,比如网易新闻客户端的图片查看界面都有这样的功能,点击一下图片,图片上方的标题栏可以隐藏,再点击一下标题栏又会出现,我接下来也要实现这个功能。动画部分我们还是使用Property Animation,我们定义进入和退出两个动画
接下来定义进入和退出方法
然后监听点击事件。这里要注意的是我们用了PhotoView这个库,这个库会拦截点击事件,所以不能直接在imageView上添加onClickListener,PhotoView 提供了setOnPhotoTapListener方法来监听点击事件。我们继续修改上面的代码。
好了,这样就能实现标题栏的隐藏和出现了。到此为止,基本功能已经全部实现,我们看一下完整的代码
一个看似简单的功能还是包含很多东西的,需要了解各种第三方库的使用,期间也碰到了很多坑。比如
图片加载我们用的是Picasso,平时用的比较多的可能就是现实图片,直接Picasso.with(this).load(url).into(imageview)就完事了,但是下载图片我们需要得到bitmap对象,所以这里就需要用Target来实现。
RxJava/RxAndroid最近很火,RxJava配合lambda变大时可以让代码变得非常简洁,但是对于刚接触的新手来说还是没那么简单的,我最近也在努力学习相关知识,代码中的异步调用尽量都用RxJava实现,由于理解不深,难免会出现滥用或者误用的情况,希望大家能够指出我的错误,也非常欢迎有兴趣的同学一起交流。
Android Studio是一个非常好用的IDE,可以自动帮我们生成很多代码,但是,初学者如果过于依赖的话也会造成负面影响。之前我新建Activity一般都会选择Basic Activity,因为很方便,AS会自动生成布局文件,Java代码和value文件,这次我心血来潮,新建了一个Empty Acticity大部分东西都得自己来写,发现很多很基础的东西都不记得了,期间碰到很多问题,所以,对新手来说最好还是自己多写写代码巩固一下知识。
Android support v7 Library里面有一个叫Palette的库,通过这个库,我们可以很方便的从图像中抽取颜色,我们看一下官方文档的介绍
A helper class to extract prominent colors from an image.
A number of colors with different profiles are extracted from the image:
1. Vibrant
2. Vibrant Dark
3. Vibrant Light
4. Muted
5. Muted Dark
6. Muted Light
These can be retrieved from the appropriate getter method.
我们这里不深究颜色是怎么提取的和每种颜色的区别,有兴趣的同学可以去查看官方文档。因为现在的背景是黑色的,所以我从图像中抽取Muted Dark这个颜色作为我们toolbar的背景。
Palette提供了四种方法生成对象
Palette generate(Bitmap bitmap)
Palette generate(Bitmap bitmap, int numColors)
generateAsync(Bitmap bitmap, PaletteAsyncListener listener)
generateAsync(Bitmap bitmap, int numColors, final PaletteAsyncListener listener)
前两种是同步方法,不过如果图片颜色比较丰富的话提取颜色的算法可能会比较耗时,所以,我们最好不要在主线程中使用。这里我们使用异步方法。
我们修改前面的加载图片方法
Picasso.with(this) .load(url) .into(new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { photoImageView.setImageBitmap(bitmap); Palette.from(bitmap).generate(palette1 -> { int colorMutedDark = palette1.getDarkMutedColor(colorDefault); toolbar.setBackgroundColor(colorMutedDark); }); if (attacher == null) { attacher = new PhotoViewAttacher(photoImageView); } else { attacher.update(); } } @Override public void onBitmapFailed(Drawable errorDrawable) { //TODO do something } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { //TODO do something } });
这样,我们的toolbar就能根据不同的图片变色了,有没有觉得很好玩呢:)。接下来,我们加点动画,让toolbar慢慢的从默认的颜色变化到指定的颜色,同时改变透明度。
这里涉及到颜色的改变,View Animation没有这个能力,我们使用Property Animation。
我们定义两个动画,因为这两个动画要一起动,所以再定义一个动画集合去控制
private ObjectAnimator colorAnimator;//toolbar变色动画 private ObjectAnimator alphaAnimator;//toolbar透明度变化动画 private AnimatorSet animatorSet;//动画集合
Palette取到颜色之后我们开始动画,所以继续修改上面的代码
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { photoImageView.setImageBitmap(bitmap); Palette.from(bitmap).generate(palette1 -> { int colorMutedDark = palette1.getDarkMutedColor(colorDefault); // toolbar.setBackgroundColor(colorMutedDark); colorAnimator = ObjectAnimator.ofInt(toolbar, "backgroundColor", colorDefault, colorMutedDark); colorAnimator.setEvaluator(new ArgbEvaluator()); alphaAnimator = ObjectAnimator.ofFloat(toolbar, "alpha", 1.0f, 0.5f); animatorSet.play(alphaAnimator).with(colorAnimator); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.setDuration(DURATION); animatorSet.start(); }); if (attacher == null) { attacher = new PhotoViewAttacher(photoImageView); } else { attacher.update(); } }
这样,我们就可以实现在DURATION时间内将toolbar的颜色变化到Palette提取出来的颜色,并将toolbar的透明度变为0.5。
接下来,我们还要做什么呢?还是加动画:)。很多app,比如网易新闻客户端的图片查看界面都有这样的功能,点击一下图片,图片上方的标题栏可以隐藏,再点击一下标题栏又会出现,我接下来也要实现这个功能。动画部分我们还是使用Property Animation,我们定义进入和退出两个动画
private ObjectAnimator inAnimator;//appbar进入动画 private ObjectAnimator exitAnimator;//appbar退出动画
接下来定义进入和退出方法
private void animIn(View view) { if (inAnimator == null) { inAnimator = ObjectAnimator.ofFloat(view, "translationY", -view.getHeight(), 0); inAnimator.setDuration(DURATION_IN_OUT); inAnimator.setInterpolator(new DecelerateInterpolator()); inAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } } }); } inAnimator.start(); } private void animOut(View view) { if (outAnimator == null) { outAnimator = ObjectAnimator.ofFloat(view, "translationY", 0, -view.getHeight()); outAnimator.setDuration(DURATION_IN_OUT); outAnimator.setInterpolator(new DecelerateInterpolator()); outAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (view.getVisibility() == View.VISIBLE) { view.setVisibility(View.GONE); } } }); } outAnimator.start(); }
然后监听点击事件。这里要注意的是我们用了PhotoView这个库,这个库会拦截点击事件,所以不能直接在imageView上添加onClickListener,PhotoView 提供了setOnPhotoTapListener方法来监听点击事件。我们继续修改上面的代码。
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { photoImageView.setImageBitmap(bitmap); Palette.from(bitmap).generate(palette1 -> { int colorMutedDark = palette1.getDarkMutedColor(colorDefault); // toolbar.setBackgroundColor(colorMutedDark); colorAnimator = ObjectAnimator.ofInt(toolbar, "backgroundColor", colorDefault, colorMutedDark); colorAnimator.setEvaluator(new ArgbEvaluator()); alphaAnimator = ObjectAnimator.ofFloat(toolbar, "alpha", 1.0f, 0.5f); animatorSet.play(alphaAnimator).with(colorAnimator); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.setDuration(DURATION); animatorSet.start(); }); if (attacher == null) { attacher = new PhotoViewAttacher(photoImageView); attacher.setOnPhotoTapListener((view, x, y) -> { if (appbar.getVisibility() == View.VISIBLE) { animOut(appbar); } else if (appbar.getVisibility() != View.VISIBLE) { animIn(appbar); } }); } else { attacher.update(); } }
好了,这样就能实现标题栏的隐藏和出现了。到此为止,基本功能已经全部实现,我们看一下完整的代码
package me.masteryi.gankio;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import butterknife.Bind;
import butterknife.ButterKnife;
import me.masteryi.gankio.utils.FileUtil;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import uk.co.senab.photoview.PhotoViewAttacher;
/**
* Created by Lee
* Date 2016/2/12
* Email jon_ly@163.com
* Blog http://masteryi.me */
public class PhotoActivity extends AppCompatActivity {
private static final String TAG = "PhotoActivity";
private static final int DURATION = 1000;//toolbar变色/透明度变化持续时间
private static final int DURATION_IN_OUT = 300;//toolbar进入/退出动画持续时间
public static final String PHOTO_URL = "photo_url";
@Bind(R.id.photo_iv)
ImageView photoImageView;
@Bind(R.id.toolbar)
Toolbar toolbar;
@Bind(R.id.appbar)
AppBarLayout appbar;
private String url;//需要显示图片url
private PhotoViewAttacher attacher;
private Palette palette;
private int colorDefault;//toolbar默认颜色
private ObjectAnimator colorAnimator;//toolbar变色动画
private ObjectAnimator alphaAnimator;//toolbar透明度变化动画
private ObjectAnimator inAnimator;//appbar进入动画
private ObjectAnimator outAnimator;//appbar退出动画
private AnimatorSet animatorSet;//动画集合
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
toolbar.setTitleTextColor(Color.WHITE);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(v -> finish());
//图片URL从其他页面传过来
url = getIntent().getStringExtra(PHOTO_URL);
//toolbar默认颜色为colorPrimary
colorDefault = ContextCompat.getColor(this, R.color.colorPrimary);
//动画集合
animatorSet = new AnimatorSet();
//加载图片
Picasso.with(this)
.load(url)
.into(new Target() {
@Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { photoImageView.setImageBitmap(bitmap); Palette.from(bitmap).generate(palette1 -> { int colorMutedDark = palette1.getDarkMutedColor(colorDefault); // toolbar.setBackgroundColor(colorMutedDark); colorAnimator = ObjectAnimator.ofInt(toolbar, "backgroundColor", colorDefault, colorMutedDark); colorAnimator.setEvaluator(new ArgbEvaluator()); alphaAnimator = ObjectAnimator.ofFloat(toolbar, "alpha", 1.0f, 0.5f); animatorSet.play(alphaAnimator).with(colorAnimator); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.setDuration(DURATION); animatorSet.start(); }); if (attacher == null) { attacher = new PhotoViewAttacher(photoImageView); attacher.setOnPhotoTapListener((view, x, y) -> { if (appbar.getVisibility() == View.VISIBLE) { animOut(appbar); } else if (appbar.getVisibility() != View.VISIBLE) { animIn(appbar); } }); } else { attacher.update(); } }
@Override
public void onBitmapFailed(Drawable errorDrawable) {
Toast.makeText(PhotoActivity.this, "加载图片出错", Toast.LENGTH_SHORT).show();
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_photo, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_download:
downloadImage();
break;
}
return super.onOptionsItemSelected(item);
}
private void downloadImage() {
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Observable.create((Observable.OnSubscribe<Boolean>) subscriber -> {
Log.d(TAG, "thread1:" + Thread.currentThread().getName());
String imageName = FileUtil.url2ImageName(url);
subscriber.onNext(FileUtil.saveImage(imageName, bitmap));
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aBoolean -> {
Log.d(TAG, "thread2:" + Thread.currentThread().getName());
if (aBoolean) {
Toast.makeText(PhotoActivity.this, "保存图片成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
}
}, throwable -> {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
Log.d(TAG, throwable.getMessage());
throwable.printStackTrace();
});
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
Toast.makeText(PhotoActivity.this, "保存图片失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso.with(this)
.load(url)
.into(target);
}
private void animIn(View view) { if (inAnimator == null) { inAnimator = ObjectAnimator.ofFloat(view, "translationY", -view.getHeight(), 0); inAnimator.setDuration(DURATION_IN_OUT); inAnimator.setInterpolator(new DecelerateInterpolator()); inAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } } }); } inAnimator.start(); } private void animOut(View view) { if (outAnimator == null) { outAnimator = ObjectAnimator.ofFloat(view, "translationY", 0, -view.getHeight()); outAnimator.setDuration(DURATION_IN_OUT); outAnimator.setInterpolator(new DecelerateInterpolator()); outAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (view.getVisibility() == View.VISIBLE) { view.setVisibility(View.GONE); } } }); } outAnimator.start(); }
}
总结
图片查看的功能已经基本实现,后面有空的话会补上收藏跟分享功能。一个看似简单的功能还是包含很多东西的,需要了解各种第三方库的使用,期间也碰到了很多坑。比如
图片加载我们用的是Picasso,平时用的比较多的可能就是现实图片,直接Picasso.with(this).load(url).into(imageview)就完事了,但是下载图片我们需要得到bitmap对象,所以这里就需要用Target来实现。
RxJava/RxAndroid最近很火,RxJava配合lambda变大时可以让代码变得非常简洁,但是对于刚接触的新手来说还是没那么简单的,我最近也在努力学习相关知识,代码中的异步调用尽量都用RxJava实现,由于理解不深,难免会出现滥用或者误用的情况,希望大家能够指出我的错误,也非常欢迎有兴趣的同学一起交流。
Android Studio是一个非常好用的IDE,可以自动帮我们生成很多代码,但是,初学者如果过于依赖的话也会造成负面影响。之前我新建Activity一般都会选择Basic Activity,因为很方便,AS会自动生成布局文件,Java代码和value文件,这次我心血来潮,新建了一个Empty Acticity大部分东西都得自己来写,发现很多很基础的东西都不记得了,期间碰到很多问题,所以,对新手来说最好还是自己多写写代码巩固一下知识。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories