git 查看远程分支、本地分支、创建分支、把分支推到远程repository、删除本地分支

查看远程分支

git branch -a

查看本地分支

git branch

创建分支

git branch testBranch //创建 git branch //查看是否创建成功

把分支推到远程分支上,让其他人下载更新

git push origin testBranch

切换到分支

git branch //先查看分支

git checkout testBranch //切换

git branch //在查看分支是否选中当前切换的分支(当前分支名称前带*号)

删除本地分支

git branch -d testBranch

删除远程版本 git push origin :名称 //删除远程分支 git branch -r -d origin/branch-name git push origin :branch-name

将List转成树的两种方式(递归、循环)

在做树结构搜索到一篇文章就拿来用了,作者写得比较通俗易懂,一看就知道算法,粘贴过来,分享分享!原文地址:http://blog.csdn.net/massivestars/article/details/53911620 在做目录树的时候通常是将一个目录存在数据库的List全部返回来,再根据节点id和parentId组装成一颗树。这里切忌使用递归查询数据库的方式实现,应把对应的目录数据全部查询回来再进行组装。List转成Tree有两种方式,一种是常用的递归,一种是双层循环。

TreeNode.java

/**
 *  佛曰:
 *       写字楼里写字间,写字间里程序员;
 *       程序人员写程序,又拿程序换酒钱。
 *       酒醒只在网上坐,酒醉还来网下眠;
 *       酒醉酒醒日复日,网上网下年复年。
 *       但愿老死电脑间,不愿鞠躬老板前;
 *       奔驰宝马贵者趣,公交自行程序员。
 *       别人笑我忒疯癫,我笑自己命太贱;
 *       不见满街漂亮妹,哪个归得程序员?
 * @author xiaobai  
 * mail:sky-xiaobai@qq.com   blog:itsayer.com
 * 2017年7月19日
 * 
 * TODO()
 */

/**
 * @author xiaobai
 * 2017年7月19日
 * @todo( )
 */

import java.util.ArrayList;  
import java.util.List; 
class TreeNode {
	private String id;
    private String parentId;  
  
    private String name;  
  
    private List<TreeNode> children;  
  
    public TreeNode(String id, String name, String parentId) {  
        this.id = id;  
        this.parentId = parentId;  
        this.name = name;  
    }  
    public TreeNode(String id, String name, TreeNode parent) {  
        this.id = id;  
        this.parentId = parent.getId();  
        this.name = name;  
    }  
  
  
    public String getParentId() {  
        return parentId;  
    }  
  
    public void setParentId(String parentId) {  
        this.parentId = parentId;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
    
    public List<TreeNode> getChildren() {  
        return children;  
    }  
  
    public void setChildren(List<TreeNode> children) {  
        this.children = children;  
    }  
  
    @Override  
    public String toString() {  
        return "TreeNode{" +  
                "id='" + id + '\'' +  
                ", parentId='" + parentId + '\'' +  
                ", name='" + name + '\'' +  
                ", children=" + children +  
                '}';  
    }  
  
}

TreeBuilder.java

import java.util.ArrayList;
import java.util.List;

/**
 *  佛曰:
 *       写字楼里写字间,写字间里程序员;
 *       程序人员写程序,又拿程序换酒钱。
 *       酒醒只在网上坐,酒醉还来网下眠;
 *       酒醉酒醒日复日,网上网下年复年。
 *       但愿老死电脑间,不愿鞠躬老板前;
 *       奔驰宝马贵者趣,公交自行程序员。
 *       别人笑我忒疯癫,我笑自己命太贱;
 *       不见满街漂亮妹,哪个归得程序员?
 * @author xiaobai  
 * mail:sky-xiaobai@qq.com   blog:itsayer.com
 * 2017年7月19日
 * 
 * TODO()
 */

/**
 * @author xiaobai
 * 2017年7月19日
 * @todo( )
 */
public class TreeBuilder {
	  
    /** 
     * 两层循环实现建树 
     * @param treeNodes 传入的树节点列表 
     * @return 
     */  
    public static List<TreeNode> bulid(List<TreeNode> treeNodes) {  
  
        List<TreeNode> trees = new ArrayList<TreeNode>();  
  
        for (TreeNode treeNode : treeNodes) {  
  
            if ("0".equals(treeNode.getParentId())) {  
                trees.add(treeNode);  
            }  
  
            for (TreeNode it : treeNodes) {  
                if (it.getParentId() == treeNode.getId()) {  
                    if (treeNode.getChildren() == null) {  
                        treeNode.setChildren(new ArrayList<TreeNode>());  
                    }  
                    treeNode.getChildren().add(it);  
                }  
            }  
        }  
        return trees;  
    }  
  
    /** 
     * 使用递归方法建树 
     * @param treeNodes 
     * @return 
     */  
    public static List<TreeNode> buildByRecursive(List<TreeNode> treeNodes) {  
        List<TreeNode> trees = new ArrayList<TreeNode>();  
        for (TreeNode treeNode : treeNodes) {  
            if ("0".equals(treeNode.getParentId())) {  
                trees.add(findChildren(treeNode,treeNodes));  
            }  
        }  
        return trees;  
    }  
  
    /** 
     * 递归查找子节点 
     * @param treeNodes 
     * @return 
     */  
    public static TreeNode findChildren(TreeNode treeNode,List<TreeNode> treeNodes) {  
        for (TreeNode it : treeNodes) {  
            if(treeNode.getId().equals(it.getParentId())) {  
                if (treeNode.getChildren() == null) {  
                    treeNode.setChildren(new ArrayList<TreeNode>());  
                }  
                treeNode.getChildren().add(findChildren(it,treeNodes));  
            }  
        }  
        return treeNode;  
    }  
  
  
  
    public static void main(String[] args) {  
  
        TreeNode treeNode1 = new TreeNode("1","广州","0");  
        TreeNode treeNode2 = new TreeNode("2","深圳","0");  
  
        TreeNode treeNode3 = new TreeNode("3","天河区",treeNode1);  
        TreeNode treeNode4 = new TreeNode("4","越秀区",treeNode1);  
        TreeNode treeNode5 = new TreeNode("5","黄埔区",treeNode1);  
        TreeNode treeNode6 = new TreeNode("6","石牌",treeNode3);  
        TreeNode treeNode7 = new TreeNode("7","百脑汇",treeNode6);  
  
  
        TreeNode treeNode8 = new TreeNode("8","南山区",treeNode2);  
        TreeNode treeNode9 = new TreeNode("9","宝安区",treeNode2);  
        TreeNode treeNode10 = new TreeNode("10","科技园",treeNode8);  
  
  
        List<TreeNode> list = new ArrayList<TreeNode>();  
  
        list.add(treeNode1);  
        list.add(treeNode2);  
        list.add(treeNode3);  
        list.add(treeNode4);  
        list.add(treeNode5);  
        list.add(treeNode6);  
        list.add(treeNode7);  
        list.add(treeNode8);  
        list.add(treeNode9);  
        list.add(treeNode10);  
  
        List<TreeNode> trees = TreeBuilder.bulid(list);  
  
        List<TreeNode> trees_ = TreeBuilder.buildByRecursive(list);  
  
        System.out.print( trees.toString() );
        
    }  
}

父亲20170717

上次父亲肠胃不好生病住院大半月,乘周末回去陪伴了几天,看到父亲的消瘦的模样,心理有说不出的痛,曾经帅气的爸爸,如今被病魔折磨得如此消瘦。肠胃不好,每顿饭只能小碗稀饭,不能进油荤,身体一天比一天差,看到我回去脸上还是挂满了笑容,我爸爸是内心藏着很多事情的人,每当独自一人总是东想西想,也许是担心自己病情,拖累我们,也许是担心这些儿女能有好的出息,总之大脑就没有闲置过,听姑姑谈起小时候的爸爸,十多岁就开始是个小大人,家里的大小事很多都是他承担着,一生都是劳累的命,从未好好的休息过,中年还为三个儿女生活费学费努力的拼搏着,经过努力的奋斗后来经济算是比较好,可是尿毒症噩耗让父亲的身体一天不如一天,把工作移交给大哥,自己回老家好好休养,还好现在至少没有工作上操劳,也至少能让他大脑能得一点闲,每次回家我感觉爸爸心理还是很高兴的,这次回去教会他使用QQ,叫他无聊可以跟我聊天,怕他一天无聊东想西想。 我希望爸爸能再多陪我们几年,今年54,我希望是60 或者更长。

android两种倒计时实现方法:Handler和Thread、CountDownTimer

介绍俩种方式,都可以实现倒计时功能。这俩种方式分别是:

    1. Handler+Thread
    2. CountDownTimer

 

1.通过使用Handler+Thread实现倒计时

        首先编写布局文件布局文件很简单,就是一个TextView,默认显示Handler获取验证码,点击TextView,进行倒计时操作,完成后恢复默认显示。
<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" >

    <TextView
        android:id="@+id/tv_show_h"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="193dp"
        android:text="Handler获取验证码" /></RelativeLayout>
        放大招,编写Activity,实现效果~
public class MainActivity extends Activity {    
private TextView tvShowH;    
private Handler handler = new Handler() {        
public void handleMessage(Message msg) {
            tvShowH.setText(msg.what - 1 + "s");            
                if (msg.what == 0) {                
                    // 倒计时结束让按钮可用
                tvShowH.setEnabled(true);
                tvShowH.setText("Handler获取验证码");
            }
        }
    };    
      @Override
    protected void onCreate(Bundle savedInstanceState) {        
                    super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvShowH = (TextView) findViewById(R.id.tv_show_h);
        tvShowH.setOnClickListener(listenerH);
    }    
           private OnClickListener listenerH = new View.OnClickListener() {        
            @Override
        public void onClick(View v) {
            tvShowH.setEnabled(false);            
                    new Thread(new Runnable() {                
                    @Override
                public void run() {                    
                    for (int i = 10; i >= 0; i--) {
                        handler.sendEmptyMessage(i);                        
                                    try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    };

}
        来张效果图瞅瞅呗~

2.通过CountDownTimer实现倒计时

CountDownTimer简介

CountDownTimer是Android内部封装好的一个关于实现倒计时功能的类。所在包:package android.os;其内部实现也是通过咱第一种实现方式,没啥好说的,看看人家官方简介吧

        官方使用方式
 new CountdownTimer(30000, 1000) {     
         public void onTick(long millisUntilFinished) {
         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
     }     public void onFinish() {
         mTextField.setText("done!");
     }
  }.start();

The calls to onTick(long) are synchronized to this object so that one call to onTick(long) won’t ever occur before the previous callback is complete. This is only relevant when the implementation of onTick(long) takes an amount of time to execute that is significant compared to the countdown interval.

从上面官方提供例子可以看出,如果想要使用CountDownTimer去实现倒计时,需要如下几个步骤:

  1. 实例化CountDownTimer对象;
  2. 提供计时时间毫秒以及时间间隔毫秒;
  3. 重写onTick()和onFinish()方法;

    那么这俩个方法分别都是什么作用呢?
    3.1 onTick(long millisUntilFinished)        参数millisUntilFinished是倒计时的剩余时间。在倒计时结束后会调用onFinish。
    3.2 onFinish()         倒计时结束后需要执行的操作可以写在这里。

  4. start()开始倒计时~
开始Coding

首先在原有界面新增一个TextView,操作流程都一样。新增后layout

<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” >    <TextView        android:id=”@+id/tv_show_c”        android:layout_width=”wrap_content”        android:layout_height=”wrap_content”        android:layout_below=”@+id/tv_show_h”        android:layout_centerHorizontal=”true”        android:layout_marginTop=”25dp”        android:text=”CountDownTimer获取验证码” />    <TextView        android:id=”@+id/tv_show_h”        android:layout_width=”wrap_content”        android:layout_height=”wrap_content”        android:layout_alignParentTop=”true”        android:layout_centerHorizontal=”true”        android:layout_marginTop=”178dp”        android:text=”Handler获取验证码” /></RelativeLayout>

        新增后Activity
public class MainActivity extends Activity {    
private TextView tvShowH, tvShowC;    
private Handler handler = new Handler() {        
public void handleMessage(Message msg) {
            tvShowH.setText(msg.what - 1 + "s");            
                    if (msg.what == 0) {                
                    // 倒计时结束让按钮可用
                tvShowH.setEnabled(true);
                tvShowH.setText("Handler获取验证码");
            }
        }
    };    @Override
    protected void onCreate(Bundle savedInstanceState) {        
          super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  
        tvShowH = (TextView) findViewById(R.id.tv_show_h);
        tvShowH.setOnClickListener(listenerH);
        tvShowC = (TextView) findViewById(R.id.tv_show_c);
        tvShowC.setOnClickListener(listenerC);
    }    private OnClickListener listenerH = new View.OnClickListener() {        
          @Override
        public void onClick(View v) {
            tvShowH.setEnabled(false);            
              new Thread(new Runnable() {                
              @Override
                public void run() {                    
                      for (int i = 10; i >= 0; i--) {
                        handler.sendEmptyMessage(i);                        
                      try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    };    private OnClickListener listenerC = new View.OnClickListener() {        
                      @Override
        public void onClick(View v) {
            tvShowC.setEnabled(false);
            timer.start();
        }
    };    private CountDownTimer timer = new CountDownTimer(10000, 1000) {        
                      @Override
        public void onTick(long millisUntilFinished) {            
                      long time = millisUntilFinished / 1000;            
                      if (time == 0) {
                tvShowC.setText(time + "秒后可重发");
                onFinish();
            }
            tvShowC.setText(time + "秒后可重发");
        }        @Override
        public void onFinish() {
            tvShowC.setEnabled(true);
            tvShowC.setText("CountDownTimer获取验证码");
        }
    };

}
        来个效果图~

不知道大家有没有发现一个小问题,怎么到1秒时,会出现短暂延迟?而且使用CountDownTimer可以显示0秒么?

关于以上问题,让我们一起去看看人家是什么写的,从他们写的代码中看看能不能发现相关蛛丝马迹~关于可以显示0秒么这个问题,个人觉得,那必须啊~就看怎么改他了~

深入了解CountDownTimer

让我们一起进入它内部去瞅瞅~

//首先就是相关的介绍,LZ英文很LOW,就不多说了~/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package android.os;//下面就是为大家简单介绍如何使用CountDownTimer去实现倒计时效果/**
 * Schedule a countdown until a time in the future, with
 * regular notifications on intervals along the way.
 *
 * Example of showing a 30 second countdown in a text field:
 *
 * <pre class="prettyprint">
 * new CountDownTimer(30000, 1000) {
 *
 *     public void onTick(long millisUntilFinished) {
 *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
 *     }
 *
 *     public void onFinish() {
 *         mTextField.setText("done!");
 *     }
 *  }.start();
 * </pre>
 *
 * The calls to {@link #onTick(long)} are synchronized to this object so that
 * one call to {@link #onTick(long)} won't ever occur before the previous
 * callback is complete.  This is only relevant when the implementation of
 * {@link #onTick(long)} takes an amount of time to execute that is significant
 * compared to the countdown interval.
 */public abstract class CountDownTimer {    /**
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;    /**
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;    
      private long mStopTimeInFuture;    
    /**
    * boolean representing if the timer was cancelled
    */
    private boolean mCancelled = false;    /**
     * @param millisInFuture The number of millis in the future from the call
     *   to {@link #start()} until the countdown is done and {@link #onFinish()}
     *   is called.
     * @param countDownInterval The interval along the way to receive
     *   {@link #onTick(long)} callbacks.
     */
    public CountDownTimer(long millisInFuture, long countDownInterval) {
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }    /**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mCancelled = true;
        mHandler.removeMessages(MSG);
    }    /**
     * Start the countdown.
     */
    public synchronized final CountDownTimer start() {
        mCancelled = false;        
          if (mMillisInFuture <= 0) {
            onFinish();            
              return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));        
      return this;
    }    /**
     * Callback fired on regular interval.
     * @param millisUntilFinished The amount of time until finished.
     */
    public abstract void onTick(long millisUntilFinished);    /**
     * Callback fired when the time is up.
     */
    public abstract void onFinish();    
      private static final int MSG = 1;    
      // handles counting down
    private Handler mHandler = new Handler() {        
      @Override
        public void handleMessage(Message msg) {            
      synchronized (CountDownTimer.this) {                
      if (mCancelled) {                    
          return;
                }                
      final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();                
          if (millisLeft <= 0) {
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {                    
      // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {                   
          long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);                    
      // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();                    
      // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };
}
CountDownTimer分析

首先从上面可以看出,一上来他就给我们进行一些简单的介绍,之后就是提供使用方法,接下来就是重点,让我们瞅瞅他们是什么写的~

  1. 方法使用synchronized修饰,保证一次操作只能有一个进行访问;
  2. 在文章开头,我简单说过他内部同样是通过Handler去实现倒计时效果,但是我们发现他使用了一个SystemClock.elapsedRealtime(),那么这个东西又是什么呢?经过百度后得知,他的作用就是返回系统启动到现在的毫秒数,包含休眠时间。不难理解,其实个人觉得和我们第一种写法差不多。
  3. 那么问题他为什么会出现短暂卡顿呢?其实大家在仔细查阅后会发现,当它等于1时,接下来再走不就是0了么,小于等于0的时候同样也会走一次,但是这次却不会更新UI,所以造成一种假象,就会让我们觉得界面出现了稍微卡顿。那么说到这,大家也就知道了怎么使用CountDownTimer去显示0.下面请看修改后的CountDownTimer~

改造后的CountDownTimer

基于以上分析,我们明白,只需要当计时毫秒数小于等于0的时候,他不会进行更新UI操作,那么我们只需要让它在小于等于0的时候,进行更新UI操作即可。

修改CountDownTimer

这部分很简单,创建一个类,将CountDownTimer中复制到我们新的类中,小小修改下即可实现我们的效果~

public abstract class CountDownTimer {    
    private final TextView test;    /**
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;    /**
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;    
      private long mStopTimeInFuture;    /**
     * boolean representing if the timer was cancelled
     */
    private boolean mCancelled = false;    /**
     * @param millisInFuture
     *            The number of millis in the future from the call to
     *            {@link #start()} until the countdown is done and
     *            {@link #onFinish()} is called.
     * @param countDownInterval
     *            The interval along the way to receive {@link #onTick(long)}
     *            callbacks.
     */
    public CountDownTimer(TextView test, long millisInFuture, long countDownInterval) {        
      this.test = test;
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }    /**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mCancelled = true;
        mHandler.removeMessages(MSG);
    }    /**
     * Start the countdown.
     */
    public synchronized final CountDownTimer start() {
        mCancelled = false;        if (mMillisInFuture <= 0) {
            onFinish();            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));        return this;
    }    /**
     * Callback fired on regular interval.
     * 
     * @param millisUntilFinished
     *            The amount of time until finished.
     */
    public void onTick(long millisUntilFinished) {        
      long time = millisUntilFinished / 1000;
        test.setText(time + "秒后可重发");
    };    /**
     * Callback fired when the time is up.
     */
    public void onFinish() {
        test.setEnabled(true);
        test.setText("完犊子");
    };    private static final int MSG = 1;    // handles counting down
    private Handler mHandler = new Handler() {        
      @Override
        public void handleMessage(Message msg) {            
          synchronized (CountDownTimer.this) {                
                if (mCancelled) {                    
                    return;
                }                
                    final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();                
                    if (millisLeft <= 0) {
                    onFinish();
                }                // else if (millisLeft < mCountdownInterval) {
                // // no tick, just delay until done
                // sendMessageDelayed(obtainMessage(MSG), millisLeft);
                // }
                else {                    
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);                    
                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();                    
                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0)
                        delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };
}

调用的时候需要传递当前TextView,计时毫秒数以及调用间隔毫秒数即可,如下:

new com.example.hlqcountdowntimer.weight.CountDownTimer(test, 10000, 1000) {        
        }.start();
        来个图瞅瞅~

android判断是否是小米系统

//判断是否是小米系统
private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";

  /**
     * 是不是小米系统
     *
     * @return
     */
    public static boolean isMIUI() {
        try {
            final BuildProperties prop = BuildProperties.newInstance();
            return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null
                    || prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null
                    || prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null;
        } catch (final IOException e) {
            return false;
        }
    }

android Bitmap的使用方法

看到一篇文章对Bitmap由浅到深的分析讲解,对于新手非常有用分享一下

  1. 初识Bitmap

Bitmap是一个final类,因此不能被继承。Bitmap只有一个构造方法,且该构造方法是没有任何访问权限修饰符修饰,也就是说该构造方法是friendly,但是谷歌称Bitmap的构造方法是private(私有的),感觉有点不严谨。不管怎样,一般情况下,我们不能通过构造方法直接新建一个Bitmap对象。 Bitmap是Android系统中的图像处理中最重要类之一。Bitmap可以获取图像文件信息,对图像进行剪切、旋转、缩放,压缩等操作,并可以以指定格式保存图像文件。

  1. 创建Bitmap对象

既然不能直接通过构造方法创建Bitmap,那怎样才能创建Bitmap对象。通常我们可以利用Bitmap的静态方法createBitmap()和BitmapFactory的decode系列静态方法创建Bitmap对象。

Bitmap的静态方法createBitmap()

createBitmap(Bitmap)
createBitmap(Bitmap,int,int,int,int)
createBitmap(Bitmap,int,int,int,int,Matrix,boolean)
createBitmap(int,int, Config)
createBitmap(DisplayMetrics,int,int ,Config )
createBitmap(int[],int,int,int,int,Config)
createBitmap(DisplayMetrics,int[],int,int,int,int,Config)
createBitmap(int[],int,int)
createBitmap(DisplayMetrics,int[],int,int,Config)
......

BitmapFactory的decode系列静态方法

Bitmap decodeFile(...)
Bitmap decodeResource(...)
Bitmap decodeByteArray(...)
Bitmap decodeStream(...)
Bitmap decodeFileDescriptor(...)
....
  1. Bitmap的颜色配置信息与压缩方式信息

Bitmap中有两个内部枚举类:Config和CompressFormat,Config是用来设置颜色配置信息的,CompressFormat是用来设置压缩方式的。

Bitmap : 1、Config->ALPHA_8 ARGB_4444 ARGB_8888 RGB_565 2、CompressFormat->JPEG PNG WEBP

Config解析:

通常我们优化Bitmap时,当需要做性能优化或者防止OOM(Out Of Memory),我们通常会使用Bitmap.Config.RGB_565这个配置,因为Bitmap.Config.ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。

Bitmap.Config.ALPHA_8:颜色信息只由透明度组成,占8位。 Bitmap.Config.ARGB_4444:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占4位,总共占16位。 Bitmap.Config.ARGB_8888:颜色信息由透明度与R(Red),G(Green),B(Blue)四部分组成,每个部分都占8位,总共占32位。是Bitmap默认的颜色配置信息,也是最占空间的一种配置。 Bitmap.Config.RGB_565:颜色信息由R(Red),G(Green),B(Blue)三部分组成,R占5位,G占6位,B占5位,总共占16位。 CompressFormat解析:

Bitmap.CompressFormat.JPEG:表示以JPEG压缩算法进行图像压缩,压缩后的格式可以是”.jpg”或者”.jpeg”,是一种有损压缩。 Bitmap.CompressFormat.PNG:表示以PNG压缩算法进行图像压缩,压缩后的格式可以是”.png”,是一种无损压缩。 Bitmap.CompressFormat.WEBP:表示以WebP压缩算法进行图像压缩,压缩后的格式可以是”.webp”,是一种有损压缩,质量相同的情况下,WebP格式图像的体积要比JPEG格式图像小40%。美中不足的是,WebP格式图像的编码时间“比JPEG格式图像长8倍”。

  1. Bitmap对图像进行操作
  2. Bitmap裁剪图像

Bitmap裁剪图像有两种方式:

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height) 根据源Bitmap对象source,创建出source对象裁剪后的图像的Bitmap。x,y分别代表裁剪时,x轴和y轴的第一个像素,width,height分别表示裁剪后的图像的宽度和高度。 注意:x+width要小于等于source的宽度,y+height要小于等于source的高度。

Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter) 这个方法只比上面的方法多了m和filter这两个参数,m是一个Matrix(矩阵)对象,可以进行缩放,旋转,移动等动作,filter为true时表示source会被过滤,仅仅当m操作不仅包含移动操作,还包含别的操作时才适用。其实上面的方法本质上就是调用这个方法而已。

public static Bitmap createBitmap(Bitmap source,int x , int y, int width,int height){
    return createBitmap(source,x,y,width,height);
}
  1. Bitmap缩放,旋转,移动图像

Bitmap缩放,旋转,移动,倾斜图像其实就是通过Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)方法实现的,只是在实现这些功能的同时还可以实现图像的裁剪。

Matrix matrix = new Matrix();
//图像缩放
matrix.postScale(0.8f,0.9f);
//图像旋转
matrix.postRotate(45);
//移动图像
matrix.postTranslate(100,80);

Bitmap bitmap = Bitmap.createBitmap(source,0,0,source.getWidth(),source.getHeight), matrix,true);

Matrix的postScale和postRotate方法还有多带两个参数的重载方法postScale(float sx, float sy, float px, float py)和postRotate(float degrees, float px, float py),后两个参数px和py都表示以该点为中心进行操作。 注意:虽然Matrix还可以调用postSkew方法进行倾斜操作,但是却不可以在此时创建Bitmap时使用。

  1. Bitmap保存图像与释放资源
bitmap = BitmapFactory.decodeResource(getResource(),R.drawable.feng);
File file = new File(getFilesDir(),"test.jpg");
if( file.exists()){
    file.delete();
}
try{
   FileOutputStream outputStream = new FileOutputStream( file );
   bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream );
   outputStream.flush();
   outputStream.close();
}catch(FileNotFoundException e){
    
}catch(IOException ex){
    
}
bitmap.recycle();
//释放资源
  1. BitmapFactory通过BitmapFactory.Options对图像进行操作

BitmapFactory是通过BitmapFactory.Options对图像进行操作的,然后将操作后的图像生成Bitmap对象或者将操作后的图像用已经存在的Bitmap保存,当不能用之保存时会返回null。 BitmapFactory.Options中常用的字段有:

inBitmap
inDensity
inDither
inJustDecodeBounds
inPreferredConfig
inSampleSize
inScaled
inTargetDensity
outHeight
outWidth

inBitmap:如果设置将会将生成的图像内容加载到该Bitmap对象中。 inDensity:给Bitmap对象设置的密度,如果inScaled为true(这是默认的),而若inDensity与inTargetDensity不匹配,那么就会在Bitmap对象返回前将其缩放到匹配inTargetDensity。 inDither:是否对图像进行抖动处理,默认值是false。 inJustDecodeBounds:如果设置成true,表示获取Bitmap对象信息,但是不将其像素加载到内存。 inPreferredConfig:Bitmap对象颜色配置信息,默认是Bitmap.Config.ARGB_8888。 inSampleSize:对图像进行压缩,设置的值为2的整数次幂或者接近2的整数次幂,当次设置为2时,宽和高为都原来的1/2,图像所占空间为原来的1/4。 inScaled:设置是否缩放。 inTargetDensity:绘制到目标Bitmap上的密度。 outHeight:Bitmap对象的高度。 outWidth:Bitmap对象的宽度。

  1. 使用Bitmap时防止OOM的有效方法
  2. 高效压缩图片
//计算BitmapFactory的 inSampleSize 的值得方法
    public int calulateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){

        if( reqWidth == 0 || reqHeight == 0  ){
            return 1;
        }
        //获取图片的原生的宽和高
        final int height = options.outHeight;
        final int width = options.outWidth;

        int inSampleSize = 1;
        //如果原生的高宽大于请求的宽高,那么将原生的宽高都置为原来的一半
        if( height>reqHeight || width >reqWidth ){

            final int halfHeight = height/2;
            final int halfWidth = width /2;

            //主要计算逻辑
            while( (halfHeight/inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth ){

                inSampleSize *=2;

            }

        }

        return inSampleSize;
    }

    public Bitmap decodeSampleBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; //只加载图像的信息
        BitmapFactory.decodeResource(res,resId,options );
        options.inSampleSize = calulateInSampleSize( options, reqWidth,reqHeight );

        options.inJustDecodeBounds = false;//加载图片包括信息和位图
        options.inPreferredConfig = Bitmap.Config.RGB_565;


        return BitmapFactory.decodeResource(res,resId,options);
    }
  1. 使用缓存

常用的缓存有内存缓存LruCache和磁盘缓存DiskLruCache。

Android界面性能调优手册 https://androidtest.org/android-graphics-performance-pattens/?utm_medium=email&utm_source=gank.io

转正地址:https://mp.weixin.qq.com/s?__biz=MzI3MDE0NzYwNA==&mid=2651435903&idx=1&sn=a24020ab22c2475e9d4ba2b4044293df&chksm=f1289a04c65f1312d352dee6c0659b98396fc62a49116f419a76da52b08cd7d5b879d0931557&mpshare=1&scene=23&srcid=0710ATL6LsWxyZLfjx3iHzbX#rd

诗在浪漫时

这一路走来,我们看了那么多的电影,读了那么多的书,听了那么多的音乐,在茫茫人海中路过那么多的人。然后在时光中,经历年少、经历青春、经历婚姻、经历中年危机、经历油盐柴米与生离死别连绵不断的呈现在眼前。当生活的可怕全然展露无遗时,你要用什么样的纯粹来荡涤这污浊与清澈夹杂的来去过往?

诗在浪漫时,而卑微者自乐。我们只好听着城市的声音,望着寂寞的人群,然后欣然前往平凡生活里的下一站。

http://www.luoo.net/vol/index/1105

谈一谈那些px、pt、ppi、dpi、dp、sp之间的关系

转自:http://www.woshipm.com/pmd/176328.html

做了几个移动端的项目之后,深感移动端尺寸换算的必要性,在此做个总结。

先介绍下各自的定义:

  • px:pixel,像素,电子屏幕上组成一幅图画或照片的最基本单元
  • pt: point,点,印刷行业常用单位,等于1/72英寸
  • ppi: pixel per inch,每英寸像素数,该值越高,则屏幕越细腻
  • dpi: dot per inch,每英寸多少点,该值越高,则图片越细腻
  • dp: dip,Density-independent pixel, 是安卓开发用的长度单位,1dp表示在屏幕像素点密度为160ppi时1px长度
  • sp: scale-independent pixel,安卓开发用的字体大小单位。

以下是换算关系:

一、pt和px

公式一: 1pt= (DPI / 72) px

当photoshop中新建画布的分辨率为72ppi( 即 72dpi时 ), 1pt=1px; 当新建画布分辨率为72*2=144ppi时,1pt=2px

二、ppi和dpi

dpi最初用于衡量打印物上每英寸的点数密度。DPI值越小图片越不精细。当DPI的概念用在计算机屏幕上时,就应称之为ppi。同理: PPI就是计算机屏幕上每英寸可以显示的像素点的数量。因此,在电子屏幕显示中提到的ppi和dpi是一样的,可认为

公式二:dpi=ppi

三、ppi计算方法

ppi是指屏幕上的像素密度,其计算方法为:

公式三: ppi= 屏幕对角线上的像素点数/对角线长度 = √ (屏幕横向像素点^2 + 屏幕纵向像素点^2)/对角线长度

以小米2s为例,该屏幕分辨率为720px*1280px,4.3英寸。则点密度为 √ (720^2 +1280^2) /4.3 = 342ppi。

四、px和dp

dp为安卓开发时的长度单位,根据不同的屏幕分辨率,与px有不同的对应关系。

安卓端屏幕大小各不相同,根据其像素密度,分为以下几种规格:

1dp定义为屏幕密度值为160ppi时的1px,即,在mdpi时,1dp = 1px。 以mdpi为标准,这些屏幕的密度值比为:ldpi : mdpi : hdpi : xhdpi : xxhdpi = 0.75 : 1 : 1.5 : 2 : 3;即,在xhdpi的密度下,1dp=2px;在hdpi情况下,1dp=1.5px。其他类推。

公式四: 1dp=(屏幕ppi/ 160)px

以WVGA屏为例,该屏幕为480px*800px,按3.8寸屏算,点密度 √ (480^2 + 800^2) / 3.8 = 245,约等于240,对应于hdpi屏幕,所以该屏幕1dp=1.5px

五、dp和sp

dp和sp都是安卓的开发单位,dp是长度单位,sp是字体单位。sp与dp类似,但是可以根据用户的字体大小首选项进行缩放。Android系统允许用户自定义文字尺寸大小(小、正常、大、超大等等),

公式五:当文字尺寸是“正常”时1sp=1dp,而当文字尺寸是“大”或“超大”时,1sp>1dp。

一般情况下可认为sp=dp。

总结:由于做设计时以xhdpi为模板,xhdpi条件下,1dp=2px。若新建画布时,将画布分辨率设为144ppi,则1pt=2px=1dp。此时,即可将pt等同于dp。标注长度的时候,将长度像素除以2即为dp值。

PS:在photoshop cc中切图时,可直接在.png 图片图层名称前加上200%获得2倍大小的图,其他比例的切图以此类推。输出的两倍图不模糊的前提是,该图是photoshop中用形状工具画出来的未被栅格化的图形,而不是已被栅格化的图层或外部导入的图片。

java中静态方法中对象锁和类锁的区别和使用

java中一个重要的知识点就是线程锁机制,为了避免数据脏读,实现同步和异步,然后类中含有静态方法的同步和非静态方法的同步有什么区别呢?下面用代码来证实一把:

class service {
    
    public synchronized void printA(){
        
        Thread.sleep(5000);
        
        System.out.print("a");
        
    }
    
    public synchronized static  void printB(){
        System.out.print("b");
    }
    
    
}

//线程A

class ThreadA extends Thread{
    Service service;
    public ThreadA( Service service ){
        this.service = service;
    }
    public void run()  {  
         service.printA();
    }  
    
}

//线程B

class ThreadB extends Thread{
    Service service;
    public ThreadB( Service service ){
        this.service = service;
    }
    public void run()  {  
         service.printB();
    }  
    
}

//运行查看结果

class Run{
    
   
    public static void main(String[] args)  
    {  
    
        Service service = new Service();  
        ThreadA threadA = new ThreadA(service);  
        threadA.start(); 
        ThreadB threadB = new ThreadB(service);  
        threadB.start();  
    }  
    
}

结果: b a

分析:为什么没有同步输出?a结果的锁是类,他静态方法也是类方法,b结果锁是对象,不是同一个锁,所以结果输出是异步执行。