您的位置:首页 > 移动开发 > Android开发

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)

前两种是同步方法,不过如果图片颜色比较丰富的话提取颜色的算法可能会比较耗时,所以,我们最好不要在主线程中使用。这里我们使用异步方法。

我们修改前面的加载图片方法

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大部分东西都得自己来写,发现很多很基础的东西都不记得了,期间碰到很多问题,所以,对新手来说最好还是自己多写写代码巩固一下知识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android