Android设置viewGroup和其子控件两者之间的焦点关系

 更新时间:2016年10月2日 16:23  点击:1844
下面我们来看一篇关于Android设置viewGroup和其子控件两者之间的焦点关系的例子,希望文章能够让各位了解到viewGroup和其子控件两者之间的关系哦。

在开发过程中,你可能会遇到这样的问题:在一个很长的界面刚初始化结束的时候默认不是最顶部而是某一个控件,比如ListView;也可能遇到Edittext自己弹出软键盘的问题等等。其实这些都是焦点获取的问题,不想有这样的小bug那么就要对焦点的获取问题进行合理的控制。大多数人会在初始化的时候就对某个控件添加这个属性:

×××.setFocusable(false);

其实在建立xml布局文件的时候Android就提供了焦点控制的属性:

android:descendantFocusability="blocksDescendants"

解释一下相应的属性和对应的表达。

该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。

属性的值有三种:


beforeDescendants:viewgroup会优先其子类控件而获取到焦点
 
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
 
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

下面我们来看一篇关于Android开发使用Netty的TCP连接中使用SSL加密的例子,希望这篇文章能够对各位有所帮助,具体的如下文介绍。

 

1 Introduction
 
数据安全在网络通信中是非常重要的一个方面。为了支持 SSL/TLS,Java 提供了 javax.net.ssl包下的类SslContext 和 SslEngine 。在Netty框架下,I/O数据在ChannelPipeline中被管道中的ChannelHandler处理并转发给下一个ChannelHandler。自然而然地,Netty也提供了ChannelHandler的实现SslHandler来支持SSL, 有一个内部 SslEngine 做实际的工作。
 
2 Steps
 
首先看看SslHandler的构造函数:
   public SslHandler(SSLEngine engine) {
        this(engine, false);
    }

    public SslHandler(SSLEngine engine, boolean startTls) {
        this(engine, startTls, ImmediateExecutor.INSTANCE);
    }
不难发现,我们需要一个SSLEngine对象来构建SslHandler。根据资料可以知道,需要根据已初始化的 SSLContext 来调用 SSLContext.createSSLEngine() 即可创建 SSLEngine。
所以基于Netty框架的在TCP连接中使用SSL/TLS加密的流程如下:
 1.在代码中导入证书,并使用该证书构造SSLContext
2.调用SSLContext对象的createSSLEngine()创建 SSLEngine
3.用SSLEngine对象去初始化Netty的SslHandler
4.在Netty的ChannelInitializer.initChannel()中,往管道(pipeline)中安装SslHandler。
 
3 Usage
 
3.1 导入证书
 public SSLContext getClientSSLContext(){
  KeyStore trustKeyStore= KeyStore.getInstance("JKS");// 访问Java密钥库,JKS是keytool创建的Java密钥库
  InputStream keyStream = MyApplication.getAppContext().getAssets().open("key.jks");//打开证书文件(.jks格式)
  char keyStorePass[]="12345678".toCharArray();  //证书密码
  keyStore .load(trustKeyStore,keyStorePass);


  TrustManagerFactory trustManagerFactory =   TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore );//保存服务端的授权证书

SSLContext   clientContext = SSLContext.getInstance( "TLS");
  clientContext.init(null, trustManagerFactory.getTrustManagers(), null);

  return clientContext;
}
在上述代码,只是实现了client采用trustKeyStore中的key.jks证书(包含了server的公钥)对数据解密,如果解密成功,证明消息来自server,进行逻辑处理。
仅仅是server->client单向的SSL认证。
如果要实现server和client之间双向的身份认证,需要模仿trustManagerFactory的初始化来构建一个KeyManagerFactory来其中保存客户端的私钥,并传入
clientContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

具体参照 SSL双向认证java实现
 
3.2 添加 SslHandler
 
ChannelHandler在应该初始化(ChannelInitializer.initChannel()被调用时)阶段被安装在ChannelPipeline中。
 

Bootstrap的介绍参考Android开发之使用Netty进行Socket编程(二)
Boostrap的初始化参考Android开发之使用Netty进行Socket编程(三)
   bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                SSLEngine engine = getClientSSLContext().createSSLEngine();
                engine.setUseClientMode(true);
                pipeline.addFirst("ssl", new SslHandler(engine));

                //....再添加其他的ChannelHandler
                pipeline.addLast(nettyChannelHandler);
            }
        });
在大多数情况下,SslHandler 将成为 ChannelPipeline 中的第一个 ChannelHandler,所以调用了pipeline.addFirst() 。这将确保所有其他 ChannelHandler 应用他们的逻辑到数据后加密后才发生,从而确保他们的变化是安全的

下面我们来看一篇android Html.fromHtml使用方法,希望文章能够让各位深入的理解到android Html.fromHtml使用细节哦。

项目中往往需要显示一段文本,如果对文本需要特定的效果,就要写自定义的span,这样的工作量会变得很大,目前android支持html格式的文本。提供了两个接口,下面我们就来看一下怎么使用。

1. Spanned android.text.Html.fromHtml(String source) //输入的参数为(html格式的文本)

目前android不支持全部的html的标签,目前只支持与文本显示和段落等标签,对于图片和其他的多媒体,还有一些自定义标签不能识别;

例子:


TextView t3 = (TextView) findViewById(R.id.text3);  
t3.setText(Html.fromHtml( "<b>text3:</b>  Text with a " + "<a href=\"http://www.google.com\">link</a> " +"created in the Java source code using HTML.")); 

另外也可以在string.xml中使用,但是要用用<!–cdata–>去转义。如下例子:


<string name="htmlFormattedText">
 <![CDATA[
 Text with markup for [b]bold[/b]
 and [i]italic[/i] text.
 
 There is also support for a
 <tt>teletype-style</tt> font.
 But no use for the <code>code</code>
 tag!
 
 ]]></string>
 
TextView view = (TextView)findViewById(R.id.sampleText);
String formattedText = getString(R.string.htmlFormattedText);
Spanned result = Html.fromHtml(formattedText);
view.setText(result);
 

2.Spanned android.text.Html.fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)

Source:    需处理的html文本
imageGetter :对图片处理(处理html中的图片标签)

tagHandler  :对标签进行处理(相当于自定义的标签处理,在这里面可以处理自定义的标签)

具体不细说,大家感兴趣可以自行学习;在实际项目里我们就可以使用第一种方式来替代之前我们用的自定义span了。

下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false">
 
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="60dp"
        android:background="@mipmap/www" />
 
 
    <LinearLayout
        android:id="@+id/ll_bottom"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:background="#F8549D"
        android:elevation="10dp"
        android:orientation="horizontal">
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:layout_weight="1">
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/ic_launcher" />
 
        </RelativeLayout>
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:layout_weight="1">
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/ic_launcher" />
 
        </RelativeLayout>
 
        <RelativeLayout
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_gravity="bottom"
            android:background="@drawable/bottom_bg_shape">
 
            <ImageView
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_centerInParent="true"
                android:src="@mipmap/icon_go" />
 
        </RelativeLayout>
 
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:layout_weight="1">
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/ic_launcher" />
 
        </RelativeLayout>
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_gravity="bottom"
            android:layout_weight="1">
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/ic_launcher" />
 
        </RelativeLayout>
    </LinearLayout>
 
</RelativeLayout>

这张图呢是我刚刚做的,效果是参考了酷狗音乐播放器来完成的,我想说明重点在底部的导航栏:如图

 再来一张反效果预览图:
 
 
2016-08-04_101104
 
 如何使得蓝色圆形的播放键的显示越过粉色的范围。当然经过合理的布局是可以达到这样的效果的,但是却有更简单的方法。也就是在根布局的属性中加入一个来自Android自身提供的属性:


android:clipChildren="false"

属性解释和说明:

1、android:clipChildren的意思:是否限制子View在其范围内

2、可以通过android:layout_gravity控制超出的部分如何显示。

3、只需在根节点设置android:clipChildren为false即可,默认为true

下面小编为各位整理一些关于安卓开发ViewPager图片预览之图片的放大缩小,移动,切换的基本知识与例子,具体的如下介绍。

1,自由的放大和缩小

2.双击放大与缩小

3.放大以后可以进行自由的移动

4.处理与ViewPager之间的的事件冲突

需要用到的知识点

1.Matrix (图片放大,缩小需要用到矩阵)

2.ScaleGestureDetector(检测用户多指触控时缩放的手势)

3.GestureDetector:检测用户双击时需要做的一些处理

4.事件分发机制(当我们图片放大时,我们的图片是可以左右移动的,在ViewPager左右切换图片,两者会有冲突)。

 


----------------------------------------------------代码设计

第一课

第一步 :自定义ImageView 实现图片自适应控件大小:(效果是:图片小于控件大小时,放大到控件大小,图片大于控件大小时,自动缩小到控件大小)


package com.example.viewpagerimage;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;


//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener{

 private boolean mOnce;//判断是否初始化
 private float mInitScale;//初始化时缩放的值
 private float mMidScale;//双击放大到达的值
 private float mMaxScale;//放大的最大值

 private Matrix mScaleMatrix;
 public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  //init
  mScaleMatrix = new Matrix();
  setScaleType(ScaleType.MATRIX);
  //当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。

 }

 public MyImageView(Context context, AttributeSet attrs) {
  this(context, attrs,0);
  // TODO Auto-generated constructor stub
 }

 public MyImageView(Context context) {
  this(context,null);
  // TODO Auto-generated constructor stub
 }
 @Override
 protected void onAttachedToWindow() {
  // TODO Auto-generated method stub
  super.onAttachedToWindow();//当View 显示在屏幕上时调用
  getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
 }
 @SuppressWarnings("deprecation")
 @Override
 protected void onDetachedFromWindow() {
  // TODO Auto-generated method stub
  super.onDetachedFromWindow();//当View从屏幕上移除时调用
  getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
 }
 /**
  * 获取ImageView加载完成的图片
  */
 @Override
 public void onGlobalLayout() {
  // 全局的布局完成后调用
  if(!mOnce){
   //得到控件的宽和高
   int width = getWidth();
   int height = getHeight();
   //得到我们的图片以及宽和高
   Drawable d = getDrawable();
   if(d == null)
    return;

   int dw = d.getIntrinsicWidth();
   int dh = d.getIntrinsicHeight();
   float scale = 1.0f;//缩放值
   //如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
   if(dw > width && dh < height){
    scale = width*1.0f/dw;
   }
   else if(dh > height && dw < width){
    scale = height*1.0f /dh;
   }

   else if(dw > width && dh > height){
    scale = Math.min(width*1.0f/dw, height*1.0f/dh);
   }
   else if(dw < width && dh < height){
    scale = Math.min(width *1.0f/dw, height*1.0f/dh);
   }
   /*
    * 得到初始化时缩放的比例
    * */

   mInitScale = scale;
   mMaxScale = mInitScale * 4;
   mMidScale = mInitScale * 2;

   //将图片移动到当前控件的中心
   int dx = getWidth()/2 - dw /2;
   int dy = getHeight()/2 - dh/2;

   mScaleMatrix.postTranslate(dx, dy);//平移
   mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
   setImageMatrix(mScaleMatrix);

   mOnce = true;

 

 

  }
 }
}

布局文件使用:


<LinearLayout 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"
    tools:context="com.example.viewpagerimage.MainActivity" >

    <com.example.viewpagerimage.MyImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="matrix"
        android:src="@drawable/viewpatherimage" />

</LinearLayout>


第二步:给自定义控件添加支持手指触控缩放的功能:(支持手指触控放大)

因为涉及到手势触摸事件所以要实现OnScaleGestureListener,OnTouchListener这两个接口。

声明成员变量: private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例

在构造函数中初始化:

mScaleGestureDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(this);

 

 

添加方法:


/**\
  * 获取当前图片的缩放值
  * @return
  */
 public float getScale(){
  float[] values = new float[9];
  mScaleMatrix.getValues(values);
  return values[Matrix.MSCALE_X];

 }

 

实现接口中的方法:

 

 

//缩放的区间,initScale maxScale
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  float scale = getScale();
  float scaleFactor = detector.getScaleFactor();//得到缩放的值

  if(getDrawable() == null){
   return true;
  }
  //缩放范围的控制
  if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
   if(scale * scaleFactor < mInitScale){
    scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
   }
   if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
    scale = mMaxScale/scale;
   }
   //缩放
   mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
   setImageMatrix(mScaleMatrix);
  }
  return true;//设置完成返回true保证事件能够进行
 }

 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  return true;//必须返回true
 }

 @Override
 public void onScaleEnd(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub

 }

 @Override
 public boolean onTouch(View v, MotionEvent event) {
  // TODO Auto-generated method stub
  mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
  return true;//必须返true
 }

 

 


全部代码如下:


package com.example.viewpagerimage;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import android.view.View.OnTouchListener;

//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener,OnTouchListener{

 private boolean mOnce;//判断是否初始化
 private float mInitScale;//初始化时缩放的值
 private float mMidScale;//双击放大到达的值
 private float mMaxScale;//放大的最大值

 private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例

 private Matrix mScaleMatrix;
 public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  //init
  mScaleMatrix = new Matrix();
  setScaleType(ScaleType.MATRIX);

  mScaleGestureDetector = new ScaleGestureDetector(context, this);
  
  setOnTouchListener(this);
  //当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。

 }

 public MyImageView(Context context, AttributeSet attrs) {
  this(context, attrs,0);
  // TODO Auto-generated constructor stub
 }

 public MyImageView(Context context) {
  this(context,null);
  // TODO Auto-generated constructor stub
 }
 @Override
 protected void onAttachedToWindow() {
  // TODO Auto-generated method stub
  super.onAttachedToWindow();//当View 显示在屏幕上时调用
  getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
 }
 @SuppressWarnings("deprecation")
 @Override
 protected void onDetachedFromWindow() {
  // TODO Auto-generated method stub
  super.onDetachedFromWindow();//当View从屏幕上移除时调用
  getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
 }
 /**
  * 获取ImageView加载完成的图片
  */
 @Override
 public void onGlobalLayout() {
  // 全局的布局完成后调用
  if(!mOnce){
   //得到控件的宽和高
   int width = getWidth();
   int height = getHeight();
   //得到我们的图片以及宽和高
   Drawable d = getDrawable();
   if(d == null)
    return;

   int dw = d.getIntrinsicWidth();
   int dh = d.getIntrinsicHeight();
   float scale = 1.0f;//缩放值
   //如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
   if(dw > width && dh < height){
    scale = width*1.0f/dw;
   }
   else if(dh > height && dw < width){
    scale = height*1.0f /dh;
   }

   else if(dw > width && dh > height){
    scale = Math.min(width*1.0f/dw, height*1.0f/dh);
   }
   else if(dw < width && dh < height){
    scale = Math.min(width *1.0f/dw, height*1.0f/dh);
   }
   /*
    * 得到初始化时缩放的比例
    * */

   mInitScale = scale;
   mMaxScale = mInitScale * 4;
   mMidScale = mInitScale * 2;

   //将图片移动到当前控件的中心
   int dx = getWidth()/2 - dw /2;
   int dy = getHeight()/2 - dh/2;

   mScaleMatrix.postTranslate(dx, dy);//平移
   mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
   setImageMatrix(mScaleMatrix);

   mOnce = true;

 

 

  }
 }
 /**\
  * 获取当前图片的缩放值
  * @return
  */
 public float getScale(){
  float[] values = new float[9];
  mScaleMatrix.getValues(values);
  return values[Matrix.MSCALE_X];

 }
 //缩放的区间,initScale maxScale
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  float scale = getScale();
  float scaleFactor = detector.getScaleFactor();//得到缩放的值

  if(getDrawable() == null){
   return true;
  }
  //缩放范围的控制
  if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
   if(scale * scaleFactor < mInitScale){
    scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
   }
   if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
    scale = mMaxScale/scale;
   }
   //缩放
   mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
   setImageMatrix(mScaleMatrix);
  }
  return true;//设置完成返回true保证事件能够进行
 }

 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  return true;//必须返回true
 }

 @Override
 public void onScaleEnd(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub

 }

 @Override
 public boolean onTouch(View v, MotionEvent event) {
  // TODO Auto-generated method stub
  mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
  return true;//必须返true
 }
}

目前实现的效果:无论手指触摸哪里都是以中心点位中心开始缩放的。

下面实现的效果是:以手指触控的任意点为中心开始缩放

第三课(第三步):支持以手指触控的任意点为中心开始缩放

关键部分是在缩放的时候不断进行边界检测,防止放大后缩小后出现白边:


/**
  * 在缩放的时候进行边界控制范围位置控制
  */

 private void checkBorderAndCenterWhenScale() {
  // TODO Auto-generated method stub
  RectF rect = getMatrixRectF();
  float deltaX = 0;
  float deltaY = 0;

  float width = getWidth();
  float height = getHeight();
  //缩放时进行边界检测,放在出现白边
  if(rect.width() >= width){
   if(rect.left > 0){//处理左边的空白
    deltaX = -rect.left;
   }
   if(rect.right < width){//处理右边的空白
    deltaX = (int) (width - rect.right);
   }
  }
  if(rect.height() >= height){
   if(rect.top > 0){
    deltaY = -rect.top;
   }
   if(rect.bottom < height){
    deltaY = height - rect.bottom;
   }
  }
  //如果宽度或高度小于控件的宽或高,则让其居中
  if(rect.width() < width){
   deltaX = width/2f -rect.right + rect.width()/2f;
  }
  if(rect.height() < height){
   deltaY = height /2f -rect.bottom + rect.height()/2f;
  }
  mScaleMatrix.postTranslate(deltaX, deltaY);

 }

全部代码:


package com.example.viewpagerimage;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;

//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener,OnTouchListener{

 private boolean mOnce;//判断是否初始化
 private float mInitScale;//初始化时缩放的值
 private float mMidScale;//双击放大到达的值
 private float mMaxScale;//放大的最大值

 private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例

 private Matrix mScaleMatrix;
 public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  //init
  mScaleMatrix = new Matrix();
  setScaleType(ScaleType.MATRIX);

  mScaleGestureDetector = new ScaleGestureDetector(context, this);

  setOnTouchListener(this);
  //当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。

 }

 public MyImageView(Context context, AttributeSet attrs) {
  this(context, attrs,0);
  // TODO Auto-generated constructor stub
 }

 public MyImageView(Context context) {
  this(context,null);
  // TODO Auto-generated constructor stub
 }
 @Override
 protected void onAttachedToWindow() {
  // TODO Auto-generated method stub
  super.onAttachedToWindow();//当View 显示在屏幕上时调用
  getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
 }
 @SuppressWarnings("deprecation")
 @Override
 protected void onDetachedFromWindow() {
  // TODO Auto-generated method stub
  super.onDetachedFromWindow();//当View从屏幕上移除时调用
  getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
 }
 /**
  * 获取ImageView加载完成的图片
  */
 @Override
 public void onGlobalLayout() {
  // 全局的布局完成后调用
  if(!mOnce){
   //得到控件的宽和高
   int width = getWidth();
   int height = getHeight();
   //得到我们的图片以及宽和高
   Drawable d = getDrawable();
   if(d == null)
    return;

   int dw = d.getIntrinsicWidth();
   int dh = d.getIntrinsicHeight();
   float scale = 1.0f;//缩放值
   //如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
   if(dw > width && dh < height){
    scale = width*1.0f/dw;
   }
   else if(dh > height && dw < width){
    scale = height*1.0f /dh;
   }

   else if(dw > width && dh > height){
    scale = Math.min(width*1.0f/dw, height*1.0f/dh);
   }
   else if(dw < width && dh < height){
    scale = Math.min(width *1.0f/dw, height*1.0f/dh);
   }
   /*
    * 得到初始化时缩放的比例
    * */

   mInitScale = scale;
   mMaxScale = mInitScale * 4;
   mMidScale = mInitScale * 2;

   //将图片移动到当前控件的中心
   int dx = getWidth()/2 - dw /2;
   int dy = getHeight()/2 - dh/2;

   mScaleMatrix.postTranslate(dx, dy);//平移
   mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
   setImageMatrix(mScaleMatrix);

   mOnce = true;

 

 

  }
 }
 /**
  * 获取当前图片的缩放值
  * @return
  */
 public float getScale(){
  float[] values = new float[9];
  mScaleMatrix.getValues(values);
  return values[Matrix.MSCALE_X];

 }
 //缩放的区间,initScale maxScale
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  float scale = getScale();
  float scaleFactor = detector.getScaleFactor();//得到缩放的值

  if(getDrawable() == null){
   return true;
  }
  //缩放范围的控制
  if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
   if(scale * scaleFactor < mInitScale){
    scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
   }
   if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
    scale = mMaxScale/scale;
   }
   //缩放,缩放中心是手指触控的地方
   mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(),detector.getFocusY());
   checkBorderAndCenterWhenScale();

   setImageMatrix(mScaleMatrix);
  }
  return true;//设置完成返回true保证事件能够进行
 }

 /**
  * 获得图片放大缩小以后的宽和高以及l r t b
  * @return
  */
 private RectF getMatrixRectF(){
  Matrix matrix = mScaleMatrix;
  RectF recF = new RectF();
  Drawable d = getDrawable();
  if(d != null){
   recF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
   matrix.mapRect(recF);
  }
  return recF;
 }
 /**
  * 在缩放的时候进行边界控制范围位置控制
  */

 private void checkBorderAndCenterWhenScale() {
  // TODO Auto-generated method stub
  RectF rect = getMatrixRectF();
  float deltaX = 0;
  float deltaY = 0;

  float width = getWidth();
  float height = getHeight();
  //缩放时进行边界检测,放在出现白边
  if(rect.width() >= width){
   if(rect.left > 0){//处理左边的空白
    deltaX = -rect.left;
   }
   if(rect.right < width){//处理右边的空白
    deltaX = (int) (width - rect.right);
   }
  }
  if(rect.height() >= height){
   if(rect.top > 0){
    deltaY = -rect.top;
   }
   if(rect.bottom < height){
    deltaY = height - rect.bottom;
   }
  }
  //如果宽度或高度小于控件的宽或高,则让其居中
  if(rect.width() < width){
   deltaX = width/2f -rect.right + rect.width()/2f;
  }
  if(rect.height() < height){
   deltaY = height /2f -rect.bottom + rect.height()/2f;
  }
  mScaleMatrix.postTranslate(deltaX, deltaY);

 }

 @Override
 public boolean onScaleBegin(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub
  return true;//必须返回true
 }

 @Override
 public void onScaleEnd(ScaleGestureDetector detector) {
  // TODO Auto-generated method stub

 }

 @Override
 public boolean onTouch(View v, MotionEvent event) {
  // TODO Auto-generated method stub
  mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
  return true;//必须返true
 }
}

[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • C#实现跨线程操作控件方法

    这篇文章主要介绍了C#实现跨线程操作控件方法,主要采用异步访问方式实现,需要的朋友可以参考下...2020-06-25
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
  • Android开发之PhoneGap打包及错误解决办法

    下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
  • 用Intel HAXM给Android模拟器Emulator加速

    Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20
  • Android判断当前屏幕是全屏还是非全屏

    在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
  • C# 如何设置label(标签)控件的背景颜色为透明

    这篇文章主要介绍了C# 如何设置label(标签)控件的背景颜色为透明,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2020-12-08