学习区干货区Android技术知识

具有细节浏览功能的图片浏览器

2016-10-05  本文已影响974人  GolferChen

前言

有网购经历的小伙伴想必对下面的情景都不陌生:当鼠标在商品图片上滑动时,下方会有一个小区域展示商品的细节。“前端”技术可以做出这个效果,但android也不是省油的,下面就带大家手工制作一个具有上述功能的app(功能更强大,还可以改变图片的透明度)。

基本知识

以下是本次开发涉及到的知识

View组件之一ImageView

ImageView的主要功能是用来显示包括图片在内的所有Drawable对象。
主要属性有:

android:scaleType  //设置图片如何缩放及移动以适应ImageView的大小

对应属性值:

android:scaleType="fitXY"  //图片纵向和横向独立缩放直至该图片完全适应该ImageView的大小
android:scaleType="fitCenter  //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在ImageView中央
android:scaleType="fitStart  //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在ImageView左上角
android:scaleType="fitEnd  //保持纵横比缩放,直至图片能完全显示在ImageView中,图片的较长边和ImageView相应边相等,并把图片放置在右上角
android:scaleType="center  //把图片放置在ImageView中央,不进行任何缩放

另一个常用的属性值

android:src//指定ImageView显示哪张图片

android:src="@drawable/ic_launcher"  //该ImageView显示drawable中名称为ic_launcher的图片

ImageView常用方法:

setImageBitmap(Bitmap)//当要显示的是位图Bitmap对象时调用此方法
setImageDrawable(Drawable)//当要显示的是Drawable对象时调用此方法
setImageResource(int)//使用图片的ID设置该ImageView显示的图片(在我的文章《使用命令行徒手开发安卓项目》中,我已说过R.id、R.id.drawable对应对象为int类型,因此读者不应对此方法的形参类型感到疑惑)
setImageAlpha(int alpha)//设置图片的透明度,透明度定义为int型
getDrawable()//从ImageView中获取包装有位图的BitmapDrawable对象

上面前三个方法作用和android:src是一样的,区别在于android:src用于xml布局文件,而这些方法用于java文件中。当需要图片随着用户的动作而变化时,就需要用到这些方法了,android:src适用于图片恒定不变的情况或者用于设置第一张图片。
第一个和第三个方法在实战中运用到,读者可参考加深印象

Bitmap对象和BitmapDrawable对象

Bitmap代表位图,BitmapDrawable对象用于封装一个Bitmap对象,它们之间的转化如下:

BitmapDrawable bitmapdrawable=new BitmapDrawable(bitmap)//BitmapDrawable的单参构造器,参数为Bitmap的一个对象
Bitmap bitmap=bitmapdrawable.getBitmap();//获取BitmapDrawable对象中所包装的Bitmap对象

要获取Bitmap对象,除了可以用上面的方法,还有以下常用方法:

createBitmap(Bitmap source, int x,int y, int width, int height)//在源位图上从坐标为(x,y)的点开始抠出宽为width,高为height的图片,保存为新的位图

想一想photoshop中的抠图对这个方法会有很好的理解,在我们制作app的过程中,我们正是使用这个方法显示一张图片的细节部分的。

事件监听器

何为事件监听器,简单来说,我们为一些组件(这些组件称为事件源)添加事件监听器后,当出现事件时(如单击按钮,触摸屏幕等),事件监听器就会调用内部的事件处理器(通常为各种自定义方法)来进行响应。
总结来说,有三要素:事件源、事件处理器,事件,我们必须三个元素都给予明确,下面详细说明如何明确。
明确事件处理器
本次开发我们使用到抽象类View.OnClickListener和View.OnTouchListener,它们的含义通过名称不难看出,在此不赘述。既然是抽象类,我们在创建它的一个对象之前,必须先改写类内部的onClick和onTouch成员方法,这其实就是在明确事件处理器。示例如下:

View.OnClickListener listener=new View.OnClickListener()//创建OnClickListener的一个对象listener
{   
 public void onClick(View view)    //重写onClick函数
   {        
      show.setText("请关注国之利刃2013");   //事件处理器定义:单击后就在id为show的文本框中显示”请关注国之利刃2013"
   }
};

指定事件源
指定事件源比较简单,只要调用组件自身提供的方法就行了。常用的方法有setOnClickListener(OnClickListener)和setOnTouchListener(OnTouchListener),set可以理解为"注册"的意思,这些方法就是为组件注册事件监听器。在我们调用这些方法时,注册组件的id就会传给onClick或onTouch函数的形参view(理解这一点对理解接下来开发应用的代码很有帮助)
明确事件:这个很简单,我们的OnClickListener和OnTouchListener就指定事件是单击还是触摸屏幕。

实战

有了以上储备知识,我们就可以着手具有细节浏览功能的图片浏览器的app的开发了。我们分xml布局文件和业务实现java文件两部分进行讲解。
xml文件中定义了四个按钮,两个单击时会切换图片,两个单击时用于改变图片透明度
除此之外,还定义了两个ImageView,一个用于展示完整图片,当触摸时会将触摸点附近的部分图片显示在另一个ImageView中,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@color/colorAccent"//为了美观,把背景色设为粉红色
tools:context=".PictureBrowser">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/next"
    android:text="下一张" />
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/next"//设置位置
    android:layout_marginLeft="50pt"//设置左留白
    android:id="@+id/last"
    android:text="上一张" />
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/next"//设置位置
    android:layout_alignLeft="@id/next"//设置该按钮与id为next的组件左对齐
    android:id="@+id/improve"
    android:text="提高透明度" />
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/improve"//设置位置
    android:layout_alignTop="@id/improve"//顶部对齐
    android:layout_alignLeft="@id/last"//左对齐
    android:id="@+id/lower"
    android:text="降低透明度" />
<ImageView
    android:layout_width="110pt"//设定ImageView的大小
    android:layout_height="100pt"
    android:layout_below="@id/lower"//位置
    android:layout_alignLeft="@id/next"//左对齐
    android:id="@+id/image1"
    android:scaleType="fitCenter"/>//设置图片放缩及移动的方式
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/image1"//位置
    android:layout_alignLeft="@id/image1"//左对齐
    android:layout_marginLeft="20pt"//左留白
    android:id="@+id/image2" />
</RelativeLayout>

java文件主要就是为各组件添加事件监听器,代码如下:

package com.golfer.www.picturebrowser;

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;


public class PictureBrowser extends AppCompatActivity {
 int[] photo=new int[]//定义图片数组,元素都是drawable里面的图片资源
{
        R.drawable.wedding2,
        R.drawable.wedding1,
        R.drawable.wedding3,
        R.drawable.wedding4,
        R.drawable.wedding5,
        R.drawable.wedding6,
        R.drawable.wedding7,
        R.drawable.wedding8,
};
Button next;
Button last;
Button improve;
Button lower;
ImageView img1;
ImageView img2;
int current=0;
int alpha=255;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    next=(Button)findViewById(R.id.next);
    last=(Button)findViewById(R.id.last);
    improve=(Button)findViewById(R.id.improve);
    lower=(Button)findViewById(R.id.lower);
    img1=(ImageView)findViewById(R.id.image1);
    img2=(ImageView)findViewById(R.id.image2);
    img1.setImageResource(photo[current]);
    View.OnClickListener click=new View.OnClickListener()
    {     //明确单击头两个按钮的单击事件的事件处理器
         public void onClick(View view)
        {
            if(view==next)//如果事件源是next按钮
            img1.setImageResource(photo[(++current)%photo.length]);//显示下一张图片
            if(view==last)//如果事件源是last按钮
            img1.setImageResource(photo[(--current)%photo.length]);//显示上一张图片
        }
     };
    next.setOnClickListener(click);//为next按钮注册事件监听器
    last.setOnClickListener(click);//为last按钮注册事件监听器
    View.OnClickListener listener=new View.OnClickListener()
    {   //明确后两个按钮的事件处理器
        public void onClick(View view)
        {
            if(view==improve)  alpha=alpha+20;//如果事件源是improve按钮,透明度加20
            if(view==lower)    alpha=alpha-20;//如果事件源是lower按钮,透明度减去20
            if(alpha>=255)   alpha=255;
            if(alpha<=0)  alpha=0;
            img1.setImageAlpha(alpha);//调用img1的设置图片透明度的方法
        }
    };
    improve.setOnClickListener(listener);//为improve按钮注册事件监听器
    lower.setOnClickListener(listener);//为lower按钮注册事件监听器
    View.OnTouchListener touch=new View.OnTouchListener()
    {  //明确触摸事件处理器
        public boolean onTouch(View view, MotionEvent event)//注意onTouch方法的返回值和onCLick不同
        {
            BitmapDrawable bitmapDrawable=    (BitmapDrawable)img1.getDrawable();//从img1中获得显示图片的BitmapDrawable对象
            Bitmap bitmap= bitmapDrawable.getBitmap();//获取BitmapDrawable中包装的Bitmap对象
            double rate=bitmap.getHeight()/img1.getHeight();
            int x=(int)(event.getX()*rate);//通过event.getX()获取触摸点的X坐标
            int y=(int)(event.getY()*rate);//event从哪里来呢?看看onTouch方法的参数吧
            if((y+120)>bitmap.getHeight())  y=bitmap.getHeight()-120;//异常处理,防止抠图越过源位图的边界
            if((x+120)>bitmap.getWidth())   x=bitmap.getWidth()-120;
            Bitmap part=Bitmap.createBitmap(bitmap,x,y,120,120);//抠取图片的一部分得到新位图
            img2.setImageBitmap(part);//这里用到ImageView显示图片的方法之一
            return true;
        }
    };
    img1.setOnTouchListener(touch);
}
}

注:读者可能会对以下几行代码有疑惑,为什么要用到这个rate(比率)呢?

  double rate=bitmap.getHeight()/img1.getHeight();
  int x=(int)(event.getX()*rate);
  int y=(int)(event.getY()*rate);

解释如下:
首先,从img1得到的位图大小和img1的大小是不同的(如果我们采用fitCenter或fitStart或fitEnd缩放方式,则图片的较长边和img1相应边相等,另一条边往往不相等)。其次,event.getX()获得的是触摸点在img1上的坐标,而我们调用createBitmap函数抠图时参数x和y要求是在位图上的坐标,因此就有必要将event.getX()乘上比率rate(位图和img1不相等的边的长度之比,在这里是它们的高之比)了。
如果我们不使用rate,将会看到img2显示的图片并不是触摸点附近的图片。

app界面及功能展示如下:


透明度正常
增大透明度
上一篇 下一篇

猜你喜欢

热点阅读