整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Android 开发中如何利用拖动的方式显示高清图片

Android 开发中如何利用拖动的方式显示高清图片?

、概述html

在android开发的过程当中,有时候会遇到这样的需求,须要显示很大的图片,而且还不容许压缩。好比显示:世界地图、微博长图等,那么如何完成这个需求呢?

首先咱们分析一下,图片很是大,考虑到内存的状况,咱们不能一次将整个图片加载到内存中,由于这样会OOM,而后图片的宽或者高超出了手机屏幕的尺寸,要想显示整张没有压缩过的图片,咱们只能每次加载图片的局部,而后监听控件的滑动事件,获得滑动的方向和距离,来从新加载图片的局部,这样咱们就能够利用拖动的方式显示整张图片了

2、BitmapRegionDecoderandroid

android加载图片的局部须要用到BitmapRegionDecoder类,BitmapRegionDecoder类主要用来加载图片的某一块矩形区域,若是你想显示图片的某一块矩形区域,那么这个类很是合适,下面来看一下它的用法:git

BitmapRegionDecoder提供了一系列的newInstance方法来构造实例,支持传入文件路径,文件描述符,文件的数据流等,以下:

public static BitmapRegionDecoder newInstance(InputStream is, boolean isShareable)
    public static BitmapRegionDecoder newInstance(String pathName, boolean isShareable)
    public static BitmapRegionDecoder newInstance(FileDescriptor fd, boolean isShareable)
    public static BitmapRegionDecoder newInstance(byte[] data, int offset, int length, boolean isShareable)

利用图片构造BitmapRegionDecoder对象后,咱们能够调用它的decodeRegion方法来加载图片中的指定区域,以下:

Bitmap bitmap=mBitmapRegionDecoder.decodeRegion(mRect, mOptions);

第一个参数指定加载区域,第二个参数是BitmapFactory.options,能够用来指定图片的解码格式,取样率等

3、开发代码ide

了解了以上内容后,咱们就能够开发加载高清大图的控件了,下面是自定义的代码,注释很详细:

package com.liunian.androidbasic.addbigimage.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.liunian.androidbasic.utils.CloseUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * Created by dell on 2018/4/12.
 */

public class BigImageView extends View {
    private Context mContext;
    private BitmapRegionDecoder mBitmapRegionDecoder;
    private Rect mRect=new Rect();
    private int mImageWidth=0;
    private int mImageHeight=0;
    private BitmapFactory.Options mOptions;

    public BigImageView(Context context) {
        this(context, null);
    }

    public BigImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BigImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext=context;
        init();
    }

    private void init() {
        // 指定图片的解码格式为RGB_565
        mOptions=new BitmapFactory.Options();
        mOptions.inPreferredConfig=Bitmap.Config.RGB_565;
    }

    // 传入须要加载的图片的inputStream
    public void setInputStream(InputStream inputStream) {
        try {
            // 先得到图片的宽高(在华为的手机上若是在构造BitmapRegionDecoder对象后得到图片宽高将获取失败)
            BitmapFactory.Options tmpOptions=new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds=true; // 将inJuestDecodeBounds设为true,这样BitmapFactory只会解析图片的原始宽/高信息,并不会真正的去加载图片
            BitmapFactory.decodeStream(inputStream, null, tmpOptions); // 解析图片得到图片的宽高
            mImageWidth=tmpOptions.outWidth; // 保存图片的宽
            mImageHeight=tmpOptions.outHeight; // 保存图片的搞

            // 根据图片对应的BitmapRegionDecoder对象
            mBitmapRegionDecoder=BitmapRegionDecoder.newInstance(inputStream, false);
            requestLayout(); // 这里调用requestLayout方法,请求从新布局,触发调用控件的onMeasure,初始化加载区域
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            CloseUtils.closeIO(inputStream);
        }
    }

    // 保存上一次事件的发生位置
    float lastEventX=0;
    float lastEventY=0;
    // 保存当前事件的发生位置
    float eventX=0;
    float eventY=0;

    // 重载控件的onTouchEvent方法,监控控件的事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 这里只处理了控件的Move事件,若是有更复杂需求,好比说根据手势缩放图片,能够将事件交由给GestureDetector处理
        if (event !=null) {
            // 获得当前事件的发生位置
            eventX=event.getX();
            eventY=event.getY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE: // move事件
                    // 根据当前事件的发生位置和上一次事件的发生位置计算出用户的滑动距离,并调整图片的加载区域
                    move((int) (lastEventX - eventX), (int) (lastEventY - eventY));
                    break;
            }
            // 保存上一次事件的发生位置
            lastEventX=event.getX();
            lastEventY=event.getY();
        }
        return true;
    }

    // 根据滑动距离调整图片的加载区域
    private void move(int moveX, int moveY) {
        // 只有当图片的高大于控件的高,控件纵向显示不下图片时,才须要调整加载区域的上下位置
        if (mImageHeight > getHeight()) {
            mRect.top=mRect.top + moveY;
            mRect.bottom=mRect.top + getHeight();
        }
        // 只有当图片的宽大于控件的宽,控件横向显示不下图片时,才须要调整加载区域的左右位置
        if (mImageWidth > getWidth()) {
            mRect.left=mRect.left + moveX;
            mRect.right=mRect.left + getWidth();
        }
        invalidate();
    }

    // 重写onDraw方法,绘制图片的局部
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mBitmapRegionDecoder !=null) {
            // 检查绘制区域的宽高,以避免绘制到图片觉得的区域
            checkHeight();
            checkWidth();
            // 加载图片的指定区域
            Bitmap bitmap=mBitmapRegionDecoder.decodeRegion(mRect, mOptions);
            Log.i("liunianprint:", "onDraw" + mRect.top + " " + mRect.left + " " + mRect.bottom + " " + mRect.right);
            // 绘制图片的局部到控件上
            if (bitmap !=null) {
                canvas.drawBitmap(bitmap, 0, 0, null);
            }
        }
    }

    /**
     * 检查加载区域是否超出图片范围
     */
    private void checkWidth() {
        // 只有当图片的宽大于控件的宽,控件横向显示不下图片时,才须要调整加载区域的左右位置
        if (mImageWidth > getWidth()) {
            if (mRect.right > mImageWidth) {
                mRect.right=mImageWidth;
                mRect.left=mRect.right - getWidth();
            }

            if (mRect.left < 0) {
                mRect.left=0;
                mRect.right=getWidth();
            }
        } else {
            mRect.left=(mImageWidth - getWidth()) / 2;
            mRect.right=mRect.left + getWidth();
        }
    }

    /**
     * 检查加载区域是否超出图片范围
     */
    private void checkHeight() {
        // 只有当图片的高大于控件的高,控件纵向显示不下图片时,才须要调整加载区域的上下位置
        if (mImageHeight > getHeight()) {
            if (mRect.bottom > mImageHeight) {
                mRect.bottom=mImageHeight;
                mRect.top=mRect.bottom - getHeight();
            }
            if (mRect.top < 0) {
                mRect.top=0;
                mRect.bottom=getHeight();
            }
        } else {
            mRect.top=(mImageHeight - getHeight()) / 2;
            mRect.bottom=mRect.top + getHeight();
        }
    }

    /**
     * 重写测量控件大小的方法,初始化图片的加载区域
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 获得控件的宽高
        int width=getMeasuredWidth(); // 使用getMeasuredWidth,不要使用getWidth方法,getWidth可能为0
        int height=getMeasuredHeight(); // getMeasuredHeight,不要使用getHeight方法,getWidth可能为0

        // 初始化图片的加载区域为图片的中心,能够自行根据需求调整
        mRect.left=(mImageWidth - width) / 2;
        mRect.right=mRect.left + width;
        mRect.top=(mImageHeight - height) / 2;
        mRect.bottom=mRect.top + height;
    }
}

使用方法

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.liunian.androidbasic.MainActivity">
    <com.liunian.androidbasic.addbigimage.view.BigImageView
        android:id="@+id/big_image_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
mBigImageView=(BigImageView) findViewById(R.id.big_image_view);
        try {
            mBigImageView.setInputStream(getAssets().open("ditu.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }

使用效果以下:

滑动控件能够加载地图的其余区域

有需要文章中完整代码的同学 现在私信发送 “底层源码” 即可免费获取

现在私信发送 “进阶” 还可以获取《更多 Android 源码解析+核心笔记+面试真题》

4、总结

一、加载高清大图须要用到BitmapRegionDecoder类,这个类能够加载图片的指定区域;

二、因为图片超出了控件的大小,咱们须要监听控件的滑动事件,实现能够经过拖动的方式显示整张图片;

三、加载图片的指定区域,得到对应的Bitmap,而后经过重写onDraw方法将Bitmap绘制到控件上

应用名称】:屏幕翻译_3_1_2285_解锁会员版
【应用版本】:3.1.2
【应用大小】:318.65MB
【适用平台】:Android
【软件标签】:#屏幕翻译

软件简介:
屏幕翻译是专为Android用户设计的实时翻译应用,旨在解决游戏、视频、聊天和网页浏览中遇到的语言障碍。此应用支持多语言功能,能够实时翻译手机屏幕上显示的文字,确保翻译结果既快速又精准。

主要功能:
实时翻译:能立即翻译屏幕上出现的文字,无需等待。
多场景适用:无论是在游戏中获取任务信息,观看外语视频,还是浏览外文网站,都能轻松应对。
用户友好设计:简洁的操作界面,使得任何用户都能快速开始使用。
高速精准:采用先进的翻译技术,提供快速而准确的翻译服务,满足日常和专业的翻译需求。

屏幕翻译v3.1.2解锁会员版为需要跨语言交流的用户提供了一个全面的翻译工具,是理想的多语言交流助手。立即体验,让语言交流无界限。



转载网站:http://kskhk.cn/news/755.html


【免责声明】

本站资源仅用于个人学习交流使用,不得用于商业用途,请在下载24小时内删除。如有侵权请及时与我们联系处理。感谢大家的支持~

soup简介

jsoup 是一款Java的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。





官网地址:
http://jsoup.org/


在官网中下载 jsou-1.8.3.jar 文件,添加到自己项目的lib库中,便可使用Jsoup提供的api,官网中也提供了一套使用指南(Cookbook),便于开发者借鉴。
Jsoup解析HTML得到一个Document对象,通过操作Document的属性来获取HTML页面内容,所以,在开始之前,先介绍一下XML中Node、Element、Document等这些相关概念的区别,防止因概念混淆而导致乱用错用。

相关概念

  • Jsoup中的继承关系

    public abstract class Node implements Cloneable
    public class Element extends Node
    public class Document extends Element

    从Jsoup源码对三者的定义可以看出如下一个树形继承关系:

  1. Node(节点)
    从上述继承关系上可以明确一点,文档中的所有内容都可以看做是一个节点。节点有很多种类型:属性节点(Attribute)、注释节点(Note)、文本节点(Text)、元素节点(Element)等,通常所说的节点是这些多种节点的统称。

  2. Element(元素)
    相比节点而言,元素则是一个更小范围的定义。元素继承于节点,是节点的子集,所以一个元素也是一个节点,节点拥有的公有属性和方法在元素中也能使用。

  3. Document(文档)
    文档继承于元素,指整个HTML文档的源码内容,通过 System.out.println(document.toString()); 即可在控制台打印出网页源码内容。

  4. 相互转换
    基于Node、Element和Document之间的“缠绵”关系,可以利用各个类中提供的方法适当转换获取所需对象,以供使用。

    使用案例

    Jsoup解析Html获取Document对象的方式分为三类:在线Url、Html文本字符串、文件,对应API如下

  • connect(String url)

  • parse(String html)

  • parse(File in, String charsetName)


    在获取到Document对象之后,可以结合HTML源码,利用Jsoup提供的api通过class、tag、id、attribute等相关属性获取对应Element,进而得到所需要的网页内容。
    下面以Jsoup的官网Cookbook页面为例,解析并获取页面目录内容。
    网页内容:





    网页源码:

    <!DOCTYPE html>
    <!-- saved from url=(0031)http://www.open-open.com/jsoup/ -->
    <html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>jsoup开发指南,jsoup中文使用手册,jsoup中文文档</title>
    <meta name="keywords" content="jsoup开发指南,jsoup中文使用手册,jsoup中文文档,jsoup java html解析器">
    <meta name="description" content="jsoup Cookbook中文版 - 由http://www.open-open.com翻译整理"><link rel="stylesheet" type="text/css" href="./jsoup开发指南,jsoup中文使用手册,jsoup中文文档_files/style.css">
    </head><body class="n1-cookbook">
    <div class="wrap">
    <div class="header">
    <div class="nav-sections">
    <ul>
    <li class="n1-home">
    <h4><a href="http://jsoup.org/">jsoup</a></h4></li>
    <li class="n1-news"><a href="http://jsoup.org/news/">新闻</a></li>
    <li class="n1-bugs"><a href="http://jsoup.org/bugs">bugs</a></li>
    <li class="n1-discussion"><a href="http://jsoup.org/discussion">讨论</a></li>
    <li class="n1-download"><a href="http://jsoup.org/download">下载</a></li>
    <li class="n1-api"><a href="http://jsoup.org/apidocs/">api参考</a></li>
    <li class="n1-cookbook"><a href="http://jsoup.org/cookbook/">Cookbook</a></li></ul>
    </div></div>
    <div class="breadcrumb"><a href="http://jsoup.org/">jsoup</a> <span class="seperator">?</span> cookbook </div>
    <div class="content">
    <div class="col1">
    <h1>jsoup Cookbook(中文版)</h1>
    <div class="toc">
    <h3>入门</h3>
    <ol start="1">
    <li><a href="http://www.open-open.com/jsoup/parsing-a-document.htm">解析和遍历一个html文档</a></li>
    </ol>
    <h3>输入</h3>
    <ol start="2">
    <li><a href="http://www.open-open.com/jsoup/parse-document-from-string.htm">解析一个html字符串</a></li>
    <li><a href="http://www.open-open.com/jsoup/parse-body-fragment.htm">解析一个body片断</a></li>
    <li><a href="http://www.open-open.com/jsoup/load-document-from-url.htm">根据一个url加载Document对象</a></li>
    <li><a href="http://www.open-open.com/jsoup/load-document-from-file.htm">根据一个文件加载Document对象</a></li>
    </ol>
    <h3>数据抽取</h3>
    <ol start="6">
    <li><a href="http://www.open-open.com/jsoup/dom-navigation.htm">使用dom方法来遍历一个Document对象</a></li>
    <li><a href="http://www.open-open.com/jsoup/selector-syntax.htm">使用选择器语法来查找元素</a></li>
    <li><a href="http://www.open-open.com/jsoup/attributes-text-html.htm">从元素集合抽取属性、文本和html内容</a></li>
    <li><a href="http://www.open-open.com/jsoup/working-with-urls.htm">URL处理</a></li>
    <li><a href="http://www.open-open.com/jsoup/example-list-links.htm">程序示例:获取所有链接</a></li>
    </ol>
    <h3>数据修改</h3>
    <ol start="11">
    <li><a href="http://www.open-open.com/jsoup/set-attributes.htm">设置属性值</a></li>
    <li><a href="http://www.open-open.com/jsoup/set-html.htm">设置元素的html内容</a></li>
    <li><a href="http://www.open-open.com/jsoup/set-text.htm">设置元素的文本内容</a></li>
    </ol>
    <h3> html清理</h3>
    <ol start="14">
    <li><a href="http://www.open-open.com/jsoup/whitelist-sanitizer.htm">消除不受信任的html (来防止xss攻击)</a></li>
    </ol><script src="./jsoup开发指南,jsoup中文使用手册,jsoup中文文档_files/ca-pub-7963911354665843.js"></script><script type="text/javascript"><!--
    google_ad_client="pub-7963911354665843";
    /* 728x90, 创建于 11-1-27 */
    google_ad_slot="5890482646";
    google_ad_width=728;
    google_ad_height=90;
    //-->
    </script>
    <script type="text/javascript" src="./jsoup开发指南,jsoup中文使用手册,jsoup中文文档_files/show_ads.js">
    </script><ins id="aswift_0_expand" style="display:inline-table;border:none;height:90px;margin:0;padding:0;position:relative;visibility:visible;width:728px;background-color:transparent"><ins id="aswift_0_anchor" style="display:block;border:none;height:90px;margin:0;padding:0;position:relative;visibility:visible;width:728px;background-color:transparent"><iframe width="728" height="90" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" onload="var i=this.id,s=window.google_iframe_oncopy,H=s&&s.handlers,h=H&&H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&&d&&(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_0" name="aswift_0" style="left:0;position:absolute;top:0;"></iframe></ins></ins></div></div>
    <div class="col2"></div></div>
    <div class="footer"><b>jsoup</b> html parser: copyright ? 2009 - 2011 <a href="http://www.open-open.com/" rel="me"><b>jonathan hedley</b></a> </div></div>
    </body></html>
    Jsoup解析:
    import java.io.IOException;
    import java.text.ParseException;
    import org.jsoup.Jsoup;
    import org.jsoup.nodes.Document;
    import org.jsoup.select.Elements;
    /**
    * @author 亦枫
    * @created_time 2016年1月5日
    * @file_user_todo Java测试类
    * @blog http://www.jianshu.com/users/1c40186e3248/latest_articles
    */
    public class JavaTest {
    /**
    * 入口函数
    * @param args
    * @throws ParseException
    */
    public static void main(String[] args) throws ParseException {
    try {
    //解析Url获取Document对象
    Document document=Jsoup.connect("http://www.open-open.com/jsoup/").get();
    //获取网页源码文本内容
    System.out.println(document.toString());
    //获取指定class的内容指定tag的元素
    Elements liElements=document.getElementsByClass("content").get(0).getElementsByTag("li");
    for (int i=0; i < liElements.size(); i++) {
    System.out.println(i + ". " + liElements.get(i).text());
    }
    } catch (IOException e) {
    System.out.println("解析出错!");
    e.printStackTrace();
    }
    }
    }
    解析结果: