Android仿微信朋友圈的查看图片ImageView,缩放,移动,回弹,下滑退出

点击小图片转到图片查看的页面在Android开发中很常用到,抱着学习和分享的心态,在这里写下自己自定义的一个ImageView,可以实现类似微信朋友圈中查看图片的功能和效果。

主要功能需求:

1.自由缩放,有最大和最小的缩放限制
2.若图片没充满整个ImageView,则缩放过程将图片居中
3.根据当前缩放的状态,双击放大两倍或缩小到原来
4.当图片尺寸超过ImageView所能展示,图片可以移动
5.移动以及缩放过程中,图片不可脱离ImageView边缘
6.缩放到离开ImageView边缘,自动回弹
7.没有缩放状态下,下滑图片,背景色逐渐透明,达到一段距离可以退出
8.若设置了退出动画,那么将图片缩回到原来打开的位置。

实现方式:

我们在这可以通过继承ImageView,自己写一个TouchImageView来实现,功能核心的实现涉及到Touch事件,因此需要重写它的onTouchEvent方法,进行相应处理。宏观上说,就是点击小图片,然后进到新的Activiy,该Activity创建这个TouchImageView,布局代码和Activity代码不再给出,可以根据需要自由使用。

对图片的移动、缩放、位置等的控制,我们用Matrix这个类来实现,这是一个功能强大的类,这里简单的介绍一下:

在这个功能里,用的比较多的是它的这几个方法,网上有很多解释,这里简单解释一下,新手更容易理解。

首先介绍一下Matrix内部的一个3x3矩阵,它用一个float[9]的数组来表示,这里我们需要常用Matrix.getValues(float[] values)的方法来获取最新的数组数据,从而知道现在图片的放大倍数和坐标位置等。如(values[MTRANS_X],values[MTRANS_Y])表示图片左上角的位移情况,可以当做是当前这个点的坐标,(0,0)时没经过位移,在左上角。

Matrix.postScale(float sx, float sy)
Matrix.postScale(float sx, float sy, float px, float py)

这两个方法是用来进行缩放,第二个方法的后两个参数是放大的中心点。每调用一次postScale,都是与之前的倍数相乘得到最终的放大倍数,如sx*Values[Matrix.MSCALE_X] ,所以在移动过程每次进入ACTION_MOVE体,先初始化一下matrix,确保是上一次操作后的prematrix,这样得到的放大倍数才是正确的。这些地方读代码会理解更完整一点。另外,需要提一下的是,postScale会产生一定的位移,如果同时你要用postTranslate进行一些特定移动的话,可能需要消除postScale位移的影响,否则最终可能移动的位置不是你想要的,我在代码中也有体现这点。

Matrix.postTranslate(float dx, float dy)

这个方法进行图片的位移,dx和dy是相对现在位置的位移距离,根据Android中的坐标表示,dx正值表示向右移,dy正值表示向下移。

另外给出一张简单流程图,希望有用。

程序代码:

下面贴出完整代码,有点长

public class TouchImageView extends ImageView{
    
    //图片的尺寸
    private float imgHeight = 0;
    private float imgWidth = 0;
    
    private Context context;
    
    //View的尺寸
    private int viewHeight = 0;
    private int viewWidth = 0;
    
    //图片的缩放最大值
    private float maxHeight;
    private float maxWidth;
    private float minHeight;
    private float minWidth;
    
    //移动前两指直接的距离
    private double beginDistance;
    private boolean isOnePointer = true;
    
    //下滑退出的控制变量
    private boolean canQuit = false;
    private boolean tryQuit = false;
    
    //图片缩放时居中过程产生的位移量
    private float tempdy = 0;
    private float tempdx = 0;
    
    //两指的中点坐标,用于设置缩放的中心点
    private float xMid,yMid; 
    //第一根手指按下的初始坐标
    private float xDown,yDown;
    
    //目前操作的Matrix对象
    private Matrix matrix = new Matrix();
    //上一次操作的Matrix对象
    private Matrix preMatrix = new Matrix();
    
    private boolean isMovePic = false;
    private boolean isZoomPic = false;
    
    //退出时需要缩放到的位置
    private int preWidth = 0;
    private int preHeight = 0;
    private int xLocation = -1;
    private int yLocation = -1;
    
    public TouchImageView(Context context) {
        super(context);
    }

    public TouchImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        setBackgroundColor(Color.BLACK);
        setScaleType(ScaleType.FIT_CENTER); //在没有获得View尺寸来进行initBitmap前,先通过这个进行居中显示
    }
    
    GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
        
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {//单击事件
            return super.onSingleTapConfirmed(e);
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {//双击事件
            doubleClickZoom();
            return super.onDoubleTap(e);
        }
    });
    
    /**
     * 设置TouchImageView的尺寸,在onCreate阶段展示图片,需要手动设置
     * @param h
     * @param w
     */
    public void setViewSize(int h, int w){
        System.out.println("setViewSize");
        viewHeight = h;
        viewWidth = w;
        //获得View尺寸后初始化图片
        initBitmap();
        
    }
    
    //初始化图片
    public void initBitmap(){ 
        System.out.println("initBitmap");
        preMatrix.reset(); //必须有,否则会受上一张图片的影响

        //缩放到宽与控件平齐
        float scaleX = (float) viewWidth / imgWidth;
        float scaleY = (float) viewHeight / imgHeight;
        float defaultScale = scaleX < scaleY ? scaleX : scaleY;
        preMatrix.postScale(defaultScale, defaultScale);
        //平移到居中
        float tranY = (viewHeight - imgHeight*defaultScale)/2;
        preMatrix.postTranslate(0, tranY);
        
        //获取最大最小缩放尺寸
        maxHeight = imgHeight * defaultScale * 3;
        maxWidth = imgWidth * defaultScale * 3;
        minHeight = imgHeight* defaultScale / 2;
        minWidth = imgWidth * defaultScale / 2;     
        setScaleType(ScaleType.MATRIX);
        setImageMatrix(preMatrix);
    }
    
    
    @Override
    public void setImageBitmap(Bitmap bm){  //先执行这个方法,再执行initBitmap 
        super.setImageBitmap(bm);   
        imgWidth = bm.getWidth();
        imgHeight = bm.getHeight();
        System.out.println("setImageBitmap: imgWidth="+imgWidth);
        System.out.println("setImageBitmap: TimgHeightH="+imgHeight);
        if(viewHeight!=0 && viewWidth!=0) {
            initBitmap();
        } else{
            viewHeight = getHeight();
            viewWidth = getWidth();
            if(viewHeight!=0 && viewWidth!=0){
                initBitmap();
            }
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event){
        gestureDetector.onTouchEvent(event);
        switch(event.getAction() & MotionEvent.ACTION_MASK){
        case MotionEvent.ACTION_DOWN:
            matrix.set(preMatrix); //时序问题,doubleClickZoom方法中设置该项时,前面会通过ACTION_UP将preMatrix设置成matrix,故提前设置该项
            xDown = event.getX();
            yDown = event.getY();
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            isOnePointer = false;
            if(!isMovePic){         // 第二根手指放下时可能已经进入了移动模式,已经进入移动模式则不激发缩放模式
                isZoomPic = true;
            }
            if(event.getPointerCount() == 2) {  // 两根手指才进行测量与计算距离,避免缩放过程中第三根手指放下激发重新测量
                float downX1 = event.getX(0);
                float downX2 = event.getX(1);
                float downY1 = event.getY(0);
                float downY2 = event.getY(1);
                float xDistance = Math.abs(downX1-downX2);
                float yDistance = Math.abs(downY1-downY2);
                xMid = (downX1+downX2)/2;
                yMid = (downY1+downY2)/2;
                beginDistance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
            }
            break;  
        case MotionEvent.ACTION_MOVE:
            if(event.getPointerCount() == 1) {
                if(!isZoomPic) {
                    float moveX = event.getX();
                    float moveY = event.getY();
                    if(Math.abs(moveX - xDown)>10 || Math.abs(moveY - yDown)>10){
                        isZoomPic = false;
                        isMovePic = true;
                    }                   
                }
                isOnePointer = true;
            } 
            else if(event.getPointerCount() == 2){
                if(!isMovePic){
                    isZoomPic = true;
                    isMovePic = false;                      
                }
                isOnePointer = false;
            } 
            else {
                isZoomPic = false;
                isMovePic = false;
                isOnePointer = false;
            }
            if(isZoomPic && !isOnePointer) { //双指缩放图片
                matrix.set(preMatrix);
                float moveX1 = event.getX(0);
                float moveX2 = event.getX(1);
                float moveY1 = event.getY(0);
                float moveY2 = event.getY(1);
                float xDistance2 = Math.abs(moveX1-moveX2);
                float yDistance2 = Math.abs(moveY1-moveY2);
                //移动后两指间的距离
                double moveDistance = Math.sqrt(xDistance2 * xDistance2 + yDistance2 * yDistance2);                     
                float zoomScale = (float) ((float)moveDistance / beginDistance);
                matrix.postScale(zoomScale, zoomScale,xMid,yMid);               
                float[] values =  new float[9];             
                //缩放最大最小的限制
                limitZoomSize(values);
                //放大或缩小时,若高或宽没有填充完控件则过程需要居中
                placeXCenter(values);
                placeYCenter(values);
                
                //检查图片边缘有没离开边界,返回需要移动的位移量
                float dx = checkXBorder(values, 0);
                float dy = checkYBorder(values, 0);
                matrix.postTranslate(dx, dy);   
                setImageMatrix(matrix);             
            } 
            if(isMovePic && isOnePointer){ //单手指移动图片
                matrix.set(preMatrix);
                float moveX = event.getX();
                float moveY = event.getY();
                //计算位移量
                float dy = moveY - yDown;
                float dx = moveX - xDown;
                float[] values =  new float[9];
                quitViewPicture(values,dx,dy);
                //检查图片边缘有没离开边界,返回需要移动的位移量   
                dx = checkXBorder(values, dx);
                dy = checkYBorder(values, dy);          
                matrix.postTranslate(dx, dy);
                setImageMatrix(matrix);
            }
            break;
        case MotionEvent.ACTION_UP:
            float[] values =  new float[9]; 
            if(tryQuit) {
                checkQuit(values);
            } else {
                //缩小离开屏幕边缘时,松手自动放大到屏幕边缘
                autoMatchParent(values);
            }
            preMatrix.set(matrix);  
            isOnePointer = true;
            isMovePic = false;  
            isZoomPic = false;
            //将前面居中过程产生的位移量置0
            tempdy = 0;
            tempdx = 0;             
            tryQuit = false;
            break;
        }
        return true;
    }
    
    
    //下滑退出操作,松手时是否达到可以退出的条件(位移量)
    public void checkQuit(float[] values) {     
        matrix.getValues(values);
        if(canQuit) {
            if(preWidth != 0 && preHeight != 0 && xLocation != -1 && yLocation != -1) {
                setBackgroundColor(Color.argb(0, 0, 0, 0));
                float toScale;
                if(preWidth > preHeight) {
                    toScale = preWidth / (values[Matrix.MSCALE_X]*imgWidth);
                } else {
                    toScale = preHeight / (values[Matrix.MSCALE_Y]*imgHeight);
                }
                matrix.getValues(values);
                float dx = xLocation - values[Matrix.MTRANS_X];
                float dy = yLocation - values[Matrix.MTRANS_Y];
                setMyAnimation(dx,dy,toScale,toScale);
            }
            else {
                AcManager.getAcManager().popActivity((Activity)context);
            }
            
        } 
        else {
            float scale = getWidth()/(values[Matrix.MSCALE_X] * imgWidth);
            matrix.postScale(scale,scale);
            placeXCenter(values);
            placeYCenter(values);
            setImageMatrix(matrix);
        }
    }
    
    //当图片未缩放状态时,向下滑可以退出图片浏览
    public void quitViewPicture(float[] values, float dx, float dy){
        matrix.getValues(values);
        float beforeZoom = values[Matrix.MTRANS_Y];
        if((imgWidth * values[Matrix.MSCALE_X] <= getWidth()) && (imgHeight * values[Matrix.MSCALE_Y] <= getHeight()) ) {
            if(dy > 0) {
                if(dy>5) { //防止双击放大时被认为是想退出
                    tryQuit = true;
                }
                // 设置背景色透明程度
                int alpha = 255- (int) ((255 * dy)/getHeight());
                setBackgroundColor(Color.argb(alpha, 0, 0, 0));
                //float scale = values[Matrix.MSCALE_X] - dy * values[Matrix.MSCALE_X] / getHeight(); //目标缩放尺寸
                float scale = 1 - dy  / getHeight(); //需要Post的缩放尺寸          
                matrix.postScale(scale, scale);
                matrix.getValues(values);
                float dyZoom = beforeZoom - values[Matrix.MTRANS_Y]; //缩小过程会产生一定回缩的位移
                float dxZoom = getWidth()/2 -imgWidth * values[Matrix.MSCALE_X]/2 ;
                matrix.postTranslate(dx + dxZoom, dy + dyZoom);     
                //placeXCenter(values);
                if(dy > 200) {
                    canQuit = true;
                } else {
                    canQuit = false;
                }
            }
        }
    }
    
    //双击放大一倍
    public void doubleClickZoom(){
        matrix.set(preMatrix);
        float[] values = new float[9];
        matrix.getValues(values);
        float currentWidth = values[Matrix.MSCALE_X] * imgWidth;
        if(currentWidth > getWidth()+ 5) {
            float scale = getWidth()/currentWidth;
            matrix.postScale(scale,scale);
            placeXCenter(values);
            placeYCenter(values);
            setImageMatrix(matrix);
            preMatrix.set(matrix);
        } else {            
            matrix.postScale(2.0f,2.0f);
            matrix.getValues(values);
            float dx = getWidth()/2 - (imgWidth*values[Matrix.MSCALE_X]/2 + values[Matrix.MTRANS_X]);
            float dy = getHeight()/2 - (imgHeight*values[Matrix.MSCALE_Y]/2 + values[Matrix.MTRANS_Y]);
            matrix.postTranslate(dx, dy);
            setImageMatrix(matrix);
            preMatrix.set(matrix);
        }
    }
    
    //缩小离开屏幕边缘时,松手自动放大到屏幕边缘
    public void autoMatchParent(float[] values) {
        matrix.getValues(values);
        float currentHeight = values[Matrix.MSCALE_Y] * imgHeight;
        float currentWidth = values[Matrix.MSCALE_X] * imgWidth;
        if(currentHeight < (getHeight()-5) && currentWidth < (getWidth()-5)) {
            float scale = getWidth()/currentWidth;
            matrix.postScale(scale,scale);
            placeXCenter(values);
            placeYCenter(values);
            setImageMatrix(matrix);
        }
    }
    
    //缩放最大最小的限制
    public void limitZoomSize(float[] values) {
        matrix.getValues(values);
        if(values[Matrix.MSCALE_Y] * imgHeight > maxHeight || values[Matrix.MSCALE_X] * imgWidth > maxWidth) { 
            float scaleX = maxWidth / (imgWidth*values[Matrix.MSCALE_X]);
            float scaleY = maxHeight / (imgHeight*values[Matrix.MSCALE_Y]);
            matrix.postScale(scaleX, scaleY,xMid,yMid); 
        } else if(values[Matrix.MSCALE_Y] * imgHeight < minHeight || values[Matrix.MSCALE_X] * imgWidth < minWidth) {
            float scaleX = minWidth / (imgWidth*values[Matrix.MSCALE_X]);
            float scaleY = minHeight / (imgHeight*values[Matrix.MSCALE_Y]);
            matrix.postScale(scaleX, scaleY,xMid,yMid);
        }
    }
    
    //X方向上缩放过程中若未充满控件宽度,那么居中缩放
    public void placeXCenter(float[] values){
        //获得最新的values
        matrix.getValues(values);
        //图片横向能完全显示时,需要居中
        if(imgWidth*values[Matrix.MSCALE_X] < getWidth() + 1) {
            System.out.println("placeXCenter:横向正在居中");
            float dx = getWidth()/2 - (imgWidth*values[Matrix.MSCALE_X]/2 + values[Matrix.MTRANS_X]);
            tempdx = dx;
            matrix.postTranslate(dx, 0);
        }
        else {                                              
            matrix.postTranslate(tempdx, 0);    //图像的横向向从未充满屏幕到充满屏幕过程中,由于居中会产生的一定位移tempdx,需要补上,否则会跳变            
        }
    }
    
    //Y方向上缩放过程中若未充满控件宽度,那么居中缩放
    public void placeYCenter(float[] values){
        //获得最新的values
        matrix.getValues(values);
        //图片纵向能完全显示时,需要居中
        if(imgHeight*values[Matrix.MSCALE_Y] < getHeight() + 1) {
            System.out.println("placeYCenter:纵向正在居中");
            float dy = getHeight()/2 - (imgHeight*values[Matrix.MSCALE_Y]/2 + values[Matrix.MTRANS_Y]);
            tempdy = dy;
            matrix.postTranslate(0, dy);                
        } else {    
            matrix.postTranslate(0, tempdy);    //图像的纵向从未充满屏幕到充满屏幕过程中,由于居中会产生的一定位移tempdy,需要补上,否则会跳变
        }
    }
    
    //X方向上的边缘检测
    public float checkXBorder(float[] values, float dx) {
        //获得最新的values
        matrix.getValues(values);
        if(imgWidth*values[Matrix.MSCALE_X] < getWidth()){ //图片宽度小于控件宽度时,不移动
            dx = 0;
        }
        else if(values[Matrix.MTRANS_X]+dx>0) { //图片右移后若离开控件左边缘,那么将图片移动对齐到左边缘
            dx = -values[Matrix.MTRANS_X];
        } 
        else if(imgWidth*values[Matrix.MSCALE_X]+values[Matrix.MTRANS_X]+ dx< getWidth()){ //图片左移后若离开控件右边缘,那么将图片移动对齐到右边缘
            dx = - imgWidth*values[Matrix.MSCALE_X] + getWidth() - values[Matrix.MTRANS_X];
        }
        return dx;
    }
    
    //Y方向上的边缘检测
    public float checkYBorder(float[] values, float dy) {       
        //获得最新的values
        matrix.getValues(values);
        if(imgHeight*values[Matrix.MSCALE_Y] < getHeight()){ //图片高度小于控件宽度时,不移动
            dy = 0;
        }
        else if(values[Matrix.MTRANS_Y]+dy>0) { //图片下移后若离开控件上边缘,那么将图片移动对齐到上边缘
            dy = -values[Matrix.MTRANS_Y];
        } 
        else if(imgHeight * values[Matrix.MSCALE_Y] + values[Matrix.MTRANS_Y] + dy < getHeight()){ //图片上移后若离开控件下边缘,那么将图片移动对齐到下边缘
            dy = - imgHeight*values[Matrix.MSCALE_Y] + getHeight() - values[Matrix.MTRANS_Y];
        }
        return dy;
    }

    /**
     * 设置退出动画需要缩放到的位置 , 需要设置才有这个效果
     * @param x 相对屏幕的绝对位置坐标x
     * @param y 相对屏幕的绝对位置坐标y
     * @param height
     * @param width
     */
    public void setQuitAnimation(int x, int y, int height, int width) {
        preWidth = width;
        preHeight = height;
        xLocation = x;
        yLocation = y;
    }
        
    public void setMyAnimation (float toTranslateX, float toTranslateY, float toScaleX, float toScaleY) {
        
        PropertyValuesHolder translateX = PropertyValuesHolder.ofFloat("translateX", 0.0f,toTranslateX);
        PropertyValuesHolder translateY = PropertyValuesHolder.ofFloat("translateY", 0.0f,toTranslateY);
        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,toScaleX);
        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,toScaleY);
        ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(translateX,translateY,scaleX,scaleY);
        animator.addUpdateListener ( new MyAnimatorListener ( getImageMatrix() ) );
        animator.setDuration ( 150 );
        animator.setInterpolator ( new LinearInterpolator () );
        animator.setStartDelay ( 0 );
        animator.start ();
        animator.addListener(new AnimatorListener(){

            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                AcManager.getAcManager().popActivity((Activity)context);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
            
        });
    }
    
    class MyAnimatorListener implements AnimatorUpdateListener {
        private Matrix mMatrix;
        
        public MyAnimatorListener(Matrix matrix) {
                mMatrix = new Matrix(matrix);
            }
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float dx =  (float) animation.getAnimatedValue("translateX"); 
            float dy =  (float) animation.getAnimatedValue("translateY"); 
            float scaleX =  (float) animation.getAnimatedValue("scaleX"); 
            float scaleY =  (float) animation.getAnimatedValue("scaleY"); 
            Matrix matrix = new Matrix(mMatrix);
            float[] values = new float[9];
            matrix.getValues(values);
            float beforeX = values[Matrix.MTRANS_X];
            float beforeY = values[Matrix.MTRANS_Y];
            matrix.postScale(scaleX,scaleY);
            matrix.getValues(values);
            matrix.postTranslate(beforeX - values[Matrix.MTRANS_X] , beforeY - values[Matrix.MTRANS_Y]);
            matrix.postTranslate(dx, dy);
            setImageMatrix(matrix);
        }
        
    }
    
    /**
     * 获取当前图片的高
     * @return
     */
    public float getCurrentImageHeight(){
        float[] values = new float[9];
        matrix.getValues(values);
        return imgHeight*values[Matrix.MSCALE_Y];   
    }
    
    /**
     * 获取当前图片的宽
     * @return
     */
    public float getCurrentImageWidth(){
        float[] values = new float[9];
        matrix.getValues(values);
        return imgWidth*values[Matrix.MSCALE_X];    
    }

}

图片退出动画

然后如果要在onCreate里设置图片的话,由于此时是获得的ImageView的尺寸是0,所以要在Activity的onCreate中用ViewTreeObserver来获取尺寸:

ViewTreeObserver viewTreeObserver = pic.getViewTreeObserver();  
        viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {  
  
                    @Override  
                    public void onGlobalLayout() {  
                        pic.setViewSize(pic.getHeight(),pic.getWidth());
                    }  
        });

如果要设置下滑退出,缩回到原来小图片位置的动画,需要在Activity中设置此项:

//xLocation和yLocation是原来的小图片相对于屏幕的坐标
//preHeight和preWidth是小图片的高和宽
pic.setQuitAnimation(xLocation, yLocation, preHeight, preWidth);

由于传入的是相对屏幕的坐标,所以TouchImageView的大小也要是整个屏幕,这样动画才会跑到正确的位置。在TouchImageView对应的Activity中设置:

getWindow().setFlags(WindowManager.LayoutParams. FLAG_FULLSCREEN , WindowManager.LayoutParams. FLAG_FULLSCREEN); 

另外,需要把TouchImageView对应的Activity的背景色设置为透明,在AndroidManifest.xml文件中对该Activity设置:

android:theme="@android:style/Theme.Translucent.NoTitleBar"

最后最后,其实代码中已经有比较多的注释了,大家可以看看。希望通过写文章,能加深自己的理解以及发现自己的错误,在学习的道路上不断前行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,858评论 25 707
  • 手势图片控件 PinchImageView 点击图片框架 photoView packagecom.example...
    Ztufu阅读 720评论 0 1
  • 基础储备 在 ImageView 中有一个成员变量mDrawMatrix,这个变量是Matrix(矩阵)类型,我们...
    Lstone阅读 3,115评论 0 5
  • CSDN博客 img cquwentao android matrix 最全方法详解与进阶(完整篇) 发表于201...
    北风知我意阅读 4,803评论 0 0
  • 如果突然失去目前的工作或者主要收入来源,你还能靠什么生存下去? 如果失去目前的工作 1.我可以很快找到一份待遇相当...
    梓毓爸阅读 200评论 0 1