代码拉取完成,页面将自动刷新
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>I'M Justin</title>
<subtitle>Android Developer</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://blog.ocrnote.com/"/>
<updated>2020-01-15T03:16:51.288Z</updated>
<id>https://blog.ocrnote.com/</id>
<author>
<name>Justin Lin</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>动态编程(一):反射</title>
<link href="https://blog.ocrnote.com/2019/01/04/dong-tai-bian-cheng-yi-fan-she/"/>
<id>https://blog.ocrnote.com/2019/01/04/dong-tai-bian-cheng-yi-fan-she/</id>
<published>2019-01-04T09:00:32.000Z</published>
<updated>2020-01-15T03:16:51.288Z</updated>
<content type="html"><![CDATA[<h1 id="一、反射机制"><a href="#一、反射机制" class="headerlink" title="一、反射机制"></a>一、反射机制</h1><h2 id="1-1概念"><a href="#1-1概念" class="headerlink" title="1.1概念"></a>1.1概念</h2><p>在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 </p><p><strong>主要提供了以下功能:</strong> </p><ul><li>在运行时判断任意一个对象所属的类。</li><li>在运行时构造任意一个类的对象。</li><li>在运行时判断任意一个类所具有的成员变量和方法。</li><li>在运行时调用任意一个对象的方法 <h2 id="1-2应用场合"><a href="#1-2应用场合" class="headerlink" title="1.2应用场合"></a>1.2应用场合</h2>反射的重点在于运行时阶段,可以动态加载和获取。常见的应用场合: </li><li>访问没有权限的方法或属性</li><li>编码阶段不能确定需要创建的类</li><li>处理注解</li><li>动态代理<h2 id="1-3特点"><a href="#1-3特点" class="headerlink" title="1.3特点"></a>1.3特点</h2></li></ul><p><strong>优点:</strong><br>提升程序的灵活性和扩展性<br><strong>缺点:</strong><br>破坏代码的封装性和可读性<br>性能差。反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。</p><h1 id="二、API"><a href="#二、API" class="headerlink" title="二、API"></a>二、API</h1><h2 id="2-1常用类"><a href="#2-1常用类" class="headerlink" title="2.1常用类"></a>2.1常用类</h2><pre><code>java.lang.Class; //类 java.lang.reflect.Constructor;//构造方法 java.lang.reflect.Field; //类的成员变量 java.lang.reflect.Method;//类的方法java.lang.reflect.Modifier;//访问权限</code></pre><h2 id="2-2获取Class对象"><a href="#2-2获取Class对象" class="headerlink" title="2.2获取Class对象"></a>2.2获取Class对象</h2><pre><code>/** * 方式一:调用某个对象的getClass()方法 */User p=new User();Class clazz=user.getClass();/** * 方式二:调用某个类的class属性来获取该类对应的Class对象 */Class clazz=User.class;/** * 使用反射:使用Class类中的forName()静态方法; */Class clazz=Class.forName("com.lin.app.test.User");</code></pre><h2 id="2-3-Class-API"><a href="#2-3-Class-API" class="headerlink" title="2.3 Class API"></a>2.3 Class API</h2><pre><code>//是否是基础类型boolean isPrimitive = class1.isPrimitive();//是否是集合类boolean isArray = class1.isArray();//是否是注解类boolean isAnnotation = class1.isAnnotation();//是否是接口类boolean isInterface = class1.isInterface();//是否是枚举类boolean isEnum = class1.isEnum();//是否是匿名内部类boolean isAnonymousClass = class1.isAnonymousClass();//是否被某个注解类修饰boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//获取class名字 包含包名路径String className = class1.getName();//获取class的包信息Package aPackage = class1.getPackage();//获取class类名String simpleName = class1.getSimpleName();//获取class访问权限int modifiers = class1.getModifiers();//内部类Class<?>[] declaredClasses = class1.getDeclaredClasses();//外部类Class<?> declaringClass = class1.getDeclaringClass();</code></pre><h2 id="2-4创建类实例"><a href="#2-4创建类实例" class="headerlink" title="2.4创建类实例"></a>2.4创建类实例</h2><pre><code> /** * 方式一:使用默认构造函数创建 * 调用Class.newInstance() * 或通过Constructor.newInstance */ try { Class clazz = User.class; User user = (User) clazz.newInstance(); Constructor constructor = clazz.getConstructor(); User user1 = (User) constructor.newInstance(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } /** * 方式二:使用带参构造函数创建 * 调用Constructor.newInstance */ try { Class clazz = User.class; Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class); User user = (User) constructor.newInstance("", ""); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); }</code></pre><h2 id="2-5方法和字段相关API"><a href="#2-5方法和字段相关API" class="headerlink" title="2.5方法和字段相关API"></a>2.5方法和字段相关API</h2><h6 id="常用Field-API"><a href="#常用Field-API" class="headerlink" title="常用Field API"></a>常用Field API</h6><pre><code> /** * Class: * Field getField(String name) 获得类声明的指定公共字段(无法获取Private字段) * Field[] getFields() 获得类声明的所有公共字段(无法获取Private字段) * Field getDeclaredField(String name) 获取类声明的指定字段 * Field[] getDeclaredFields() 获取类声明的所有字段 * * Field: * getType() 返回这个变量的类型 * getGenericType() 如果当前属性有签名属性类型就返回,否则就返回 Field.getType() * isEnumConstant() 判断这个属性是否是枚举类 * getModifiers() 获取语言修饰符 public、private等 * getName() 获取属性的名字 * get(Object obj) 获取该对象属性值 * set(Object obj, Object value) 设置该对象属性值 * setAccessible(boolean flag) 设置允许访问private对象 */</code></pre><h6 id="常用Method-API"><a href="#常用Method-API" class="headerlink" title="常用Method API"></a>常用Method API</h6><pre><code> /** * Class: * getMethods(); 获取类声明的公共方法(无法获取Private方法) * getMethod(String name, Class<?>... parameterTypes) 获取类声明的指定公共方法(无法获取Private方法) * getDeclaredMethods(); 获取类声明的所有方法 * getDeclaredMethod(String name, Class<?>... parameterTypes) 获取类声明的指定方法 * * Method: * getDeclaringClass() 返回方法所在的Class * getParameterTypes() 形参类型 * getExceptionTypes() 抛出的异常类型 * getReturnType() 返回类型 * getModifiers() 获取语言修饰符 public、private等 * getName() 获取方法名称 * setAccessible(boolean flag) 设置允许访问private对象 * isBridge() 是否是桥接方法 * isSynthetic() 是否是复合方法 * isVarArgs() 是否带有可变参数 * invoke(Object obj, Object... args)调用方法 * getAnnotation(Class<T> annotationClass) 获取注解信息 */</code></pre><h1 id="三、示例"><a href="#三、示例" class="headerlink" title="三、示例"></a>三、示例</h1><pre><code>public class Test { public void getClazz() { //方式一:使用默认构造函数创建 try { Class clazz = User.class; User user = (User) clazz.newInstance(); Constructor constructor = clazz.getConstructor(); User user1 = (User) constructor.newInstance(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //方式二:通过Constructor.newInstance创建 try { Class clazz = User.class; Constructor constructor = clazz.getDeclaredConstructor(String.class, String.class); User user = (User) constructor.newInstance("", ""); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } try { Class clazz = Class.forName("com.lin.app.test.User"); Object obj = clazz.getDeclaredConstructor(String.class, String.class).newInstance("姓名", "地址"); Field address = clazz.getDeclaredField("address"); //设置允许访问private对象 address.setAccessible(true); //获取address的值 print("before ", address.getName(), address.get(obj).toString()); //改变address的值 address.set(obj, "成都"); print("after ", address.getName(), address.get(obj).toString()); Method setMethod=clazz.getDeclaredMethod("setName",String.class); //设置允许访问private对象 setMethod.setAccessible(true); Method getMethod=clazz.getDeclaredMethod("getName",String.class); getMethod.setAccessible(true); print("before",getMethod.getName(),((String)getMethod.invoke(obj))); setMethod.invoke(obj,"张三"); print("after",getMethod.getName(),((String)getMethod.invoke(obj))); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private void print(String... msg) { StringBuilder builder = new StringBuilder(); for (String s : msg) { builder.append(s).append(" - "); } if (builder.length() > 0) { builder.deleteCharAt(builder.length() - 2); } Log.e(getClass().getSimpleName(), builder.toString()); }}public class User { private String name; private String address; public User() { } public User(String name, String address) { this.name = name; this.address = address; } private String getName() { return name; } private void setName(String name) { this.name = name; }}</code></pre>]]></content>
<summary type="html">
<h1 id="一、反射机制"><a href="#一、反射机制" class="headerlink" title="一、反射机制"></a>一、反射机制</h1><h2 id="1-1概念"><a href="#1-1概念" class="headerlink" title=
</summary>
</entry>
<entry>
<title>记录:Handler、HandlerThread</title>
<link href="https://blog.ocrnote.com/2019/01/04/ji-lu-handler-handlerthread/"/>
<id>https://blog.ocrnote.com/2019/01/04/ji-lu-handler-handlerthread/</id>
<published>2019-01-04T03:40:10.000Z</published>
<updated>2020-01-15T03:16:10.209Z</updated>
<content type="html"><![CDATA[<h1 id="Handler"><a href="#Handler" class="headerlink" title="Handler"></a>Handler</h1><h2 id="相关类"><a href="#相关类" class="headerlink" title="相关类"></a>相关类</h2><ul><li>Looper: 一个线程可以产生一个 Looper 对象,由它来管理此线程里的 MessageQueue( 消息队列 )和对消息进行循环。</li><li>Handler: 你可以构造 Handler 对象来与 Looper 沟通,以便 push 新消息到 MessageQueue 里 ; 或者接收 Looper 从 MessageQueue 取出 所送来的消息。</li><li>Message Queue( 消息队列 ): 用来存放线程放入的消息。</li><li>Message:是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想传递的消息。<h2 id="关系"><a href="#关系" class="headerlink" title="关系"></a>关系</h2></li><li>一个Thread对应多个Handler,一个Thread对应一个Looper和MessageQueue</li><li>Handler与Thread共享Looper和MessageQueue</li><li>Message只是消息的载体,将会被发送到与线程绑定的唯一的MessageQueue中,并且被与线程绑定的唯一的Looper分发,被与其自身绑定的Handler消费。<h2 id="调用流程"><a href="#调用流程" class="headerlink" title="调用流程"></a>调用流程</h2></li></ul><ol><li>当我们调用handler.sendMessage(msg)方法发送一个Message时,实际上这个Message是发送到与当前线程绑定的一个MessageQueue中</li><li>然后与当前线程绑定的Looper将会不断的从MessageQueue中取出新的Message</li><li>调用msg.target.dispathMessage(msg)方法将消息分发到与Message绑定的handler.handleMessage()方法中。<h1 id="HandlerThread"><a href="#HandlerThread" class="headerlink" title="HandlerThread"></a>HandlerThread</h1>在线程(Thread)中,默认是没有Looper,需要手动添加。<br>官方提供HandlerThread类,方便创建一个拥有Looger实例的线程,结合Handler在子线程中执行耗时或延时等任务。<pre><code>Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.</code></pre><h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2></li></ol><ul><li>HandlerThread本质上是一个线程类,它继承了Thread</li><li>拥有自己的消息队列,它不会干扰或阻塞UI线程</li><li>拥有自己的内部Looper对象,可以进行looper循环</li><li>通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务</li><li>创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。<h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2></li></ul><pre><code>class MainActivity : AppCompatActivity() { private lateinit var handlerThread: HandlerThread private lateinit var handler: Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) handlerThread = HandlerThread(javaClass.simpleName) handlerThread.start() handler = Handler(handlerThread.looper) { //处理消息 true } } override fun onDestroy() { super.onDestroy() handler.removeCallbacksAndMessages(null) handlerThread.quit() }}</code></pre><h2 id="退出循环"><a href="#退出循环" class="headerlink" title="退出循环"></a>退出循环</h2><p>HandlerThread提供两个方法退出循环。</p><h3 id="quit"><a href="#quit" class="headerlink" title="quit"></a>quit</h3><p>将不在接受新的事件加入消息队列<br>清空MessageQueue中所有消息,无论是否是延迟消息</p><h3 id="quitSafely"><a href="#quitSafely" class="headerlink" title="quitSafely"></a>quitSafely</h3><p>将不在接受新的事件加入消息队列<br>清空MessageQueue中所有的延时消息<br>API 18 引入<br><strong>区别:清空消息之前会派发所有的非延迟消息</strong> </p><pre><code> /** * Quits the looper. * <p> * Causes the {@link #loop} method to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> * * @see #quitSafely */ public void quit() { mQueue.quit(false); } /** * Quits the looper safely. * <p> * Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p> */ public void quitSafely() { mQueue.quit(true); }</code></pre>]]></content>
<summary type="html">
<h1 id="Handler"><a href="#Handler" class="headerlink" title="Handler"></a>Handler</h1><h2 id="相关类"><a href="#相关类" class="headerlink" title=
</summary>
</entry>
<entry>
<title>国际化(i18n)-数字</title>
<link href="https://blog.ocrnote.com/2018/08/15/guo-ji-hua-i18n-shu-zi/"/>
<id>https://blog.ocrnote.com/2018/08/15/guo-ji-hua-i18n-shu-zi/</id>
<published>2018-08-15T09:20:51.000Z</published>
<updated>2020-01-15T03:17:40.219Z</updated>
<content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>平常在项目中,经常会遇到String和Double间的转换,或者将Double、Float等类型格式化后显示。当我们的项目是直接在国内使用的时候,通过String.format、Double.parseDouble等方法转换通常不会出现问题。但是如果产品延伸到国外,通过这种方式通常会抛出异常、结果与实际不符等情况。本篇将整理在其它语言环境下字符和数字在实际使用中遇到的问题和采用的解决办法。 </p><h1 id="语言编码"><a href="#语言编码" class="headerlink" title="语言编码"></a>语言编码</h1><ul><li>国际化:internationalization,由于首尾字母间有18个字符,简称i18n </li><li>本地化:localization,由于首尾字母间有10个字符,简称l10n </li><li><a href="https://link.jianshu.com/?t=http://www.loc.gov/standards/iso639-2/php/English_list.php" target="_blank" rel="noopener">ISO-639</a> 标准使用编码定义了国际上常见的语言,每一种语言由两个小写字母表示。</li><li><a href="https://link.jianshu.com/?t=https://www.iso.org/obp/ui/#iso:std:iso:3166:-2:ed-3:v1:en,fr" target="_blank" rel="noopener">ISO-3166</a> 标准使用编码定义了国家/地区,每个国家/地区由两个大写字母表示。</li></ul><h3 id="常用语言编码"><a href="#常用语言编码" class="headerlink" title="常用语言编码"></a>常用语言编码</h3><table><thead><tr><th>国家/地区</th><th>语言编码</th></tr></thead><tbody><tr><td>中文</td><td>zh</td></tr><tr><td>中文(简体)</td><td>zh-CN</td></tr><tr><td>中文(香港)</td><td>zh-HK</td></tr><tr><td>中文(澳门)</td><td>zh-MO</td></tr><tr><td>英语</td><td>en</td></tr><tr><td>英语(美国)</td><td>en-us</td></tr><tr><td>法语</td><td>fr</td></tr><tr><td>德语</td><td>de</td></tr><tr><td>日语</td><td>ja</td></tr><tr><td>韩语</td><td>ko</td></tr><tr><td>俄语</td><td>ru</td></tr></tbody></table><h1 id="数字格式化"><a href="#数字格式化" class="headerlink" title="数字格式化"></a>数字格式化</h1><p>使用Kotlin作为示例代码。没做特殊说明,都采用默认中文作为环境语言。 </p><h2 id="Double转String"><a href="#Double转String" class="headerlink" title="Double转String"></a>Double转String</h2><h4 id="使用String-format:"><a href="#使用String-format:" class="headerlink" title="使用String.format:"></a>使用String.format:</h4><pre><code>val doubleValue = 1258.6999999999val doubleStr1="$doubleValue"val doubleStr2=String.format("%f",doubleValue)val doubleStr3=String.format("%1\$s",doubleValue)//保留小数点后五位val doubleStr4=String.format("%.3f",doubleValue)</code></pre><p><strong>当语言环境为中文时,结果:</strong><br>doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。<br><img src="https://img-blog.csdn.net/20180815151855351?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br><strong>当语言环境为英文时,结果:</strong><br>doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。<br>千分位没有分隔符。<br><img src="https://img-blog.csdn.net/20180815152422333?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br><strong>当语言环境为俄文时,结果:</strong><br>doubleStr2和doubleStr4采用四舍五入的方式,对字符转进行了处理。<br>千分位没有分隔符。<br>doubleStr1和doubleStr3结果与俄文格式不符。<br><img src="https://img-blog.csdn.net/2018081515212057?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br>如果不考虑精度的情况从上面三种情况可以看出,将有小数的数字转换为字符串展现出来,需要使用 <strong>String.format(“%f”,doubleValue)</strong>或<strong>String.format(“%.3f”,doubleValue)</strong>的方式。 </p><h4 id="使用NumberFormat:"><a href="#使用NumberFormat:" class="headerlink" title="使用NumberFormat:"></a>使用NumberFormat:</h4><p>使用NumberFormat,我们可以设置保留多少小数位,设置结果计算方式(RoundingMode)等 </p><pre><code>fun getDoubleStr(value: Double, digits: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.CHINA): String { try { val format = NumberFormat.getNumberInstance(locale) //等同于NumberFormat.getNumberInstance //val format = DecimalFormat.getNumberInstance(locale) //设置小数点后最小位数 format.minimumFractionDigits = digits //设置小数点后最大位数 format.maximumFractionDigits = digits format.roundingMode = roundingMode //format.isGroupingUsed=false //取消整数位分隔符 return format.format(value) } catch (e: Exception) { e.printStackTrace() } return "$value" } ... private val digits = 5//输出中文字符串val chineseStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,Locale.CHINA)//输出俄文字符串val russianStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,russianLocale)</code></pre><p><strong>结果:</strong><br><img src="https://img-blog.csdn.net/20180815161603487?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h4 id="使用DecimalFormat:"><a href="#使用DecimalFormat:" class="headerlink" title="使用DecimalFormat:"></a>使用DecimalFormat:</h4><p>占位符有0和#,当使用0的时候会严格按照样式来进行匹配,不够的时候会补0,而使用#时会将前后的0进行忽略。<br>DecimalFormat还可以对展示的字符样式进行自定义,比如设置小数分隔符、整数位分隔符用什么符号表示(Char类型),这里不做补充说明。 </p><pre><code> fun getDefaultDoubleStr(value: Double, partten: String, locale: Locale = Locale.CHINA): String { try { //val format = DecimalFormat(partten) val format = DecimalFormat.getNumberInstance(locale) as DecimalFormat format.applyPattern(partten) return format.format(value) } catch (e: Exception) { e.printStackTrace() } return "$value" }... val defaultStr=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#0.00") val defaultStr2=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#0.00#",russianLocale) //设置整数位分隔符 val defaultStr3=DoubleNationalUtil.getDefaultDoubleStr(doubleValue,"#,##0.00#")</code></pre><p><strong>结果:</strong><br><img src="https://img-blog.csdn.net/20180815162340608?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h2 id="String转Double"><a href="#String转Double" class="headerlink" title="String转Double"></a>String转Double</h2><p>将String转换为Double,这里很容易抛出异常或结果与预期不一致的情况。比如: </p><ul><li>不同的语言间进行转换 </li><li>当前程序语言和系统语言不一致,使用Double. parseDouble方法 </li></ul><h4 id="使用Double-parseDouble:"><a href="#使用Double-parseDouble:" class="headerlink" title="使用Double. parseDouble:"></a>使用Double. parseDouble:</h4><pre><code>try { //chineseStr=1,258.69999 java.lang.Double.parseDouble(chineseStr) //russianStr=1 258,69999 java.lang.Double.parseDouble(russianStr) }catch (e:Exception){ e.printStackTrace() }</code></pre><p>这里不管是先转换中文String还是俄文String,结果都会抛出异常。<br>不管是中文环境下,还是俄文环境下,结果都会抛出异常。<br>所以不建议大家采用Double. parseDouble方法将String转换为Double<br><img src="https://img-blog.csdn.net/20180815164638158?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h4 id="使用NumberFormat:-1"><a href="#使用NumberFormat:-1" class="headerlink" title="使用NumberFormat:"></a>使用NumberFormat:</h4><p>下面使用NumberFormat转换,查看语言不一致的输出结果。<br>为方便阅读,命名方式没有按照驼峰命名,请大家忽略。 </p><pre><code>fun getDouble(value: String, locale: Locale = Locale.CHINA): Double { try { val format = NumberFormat.getNumberInstance(locale) //等同于NumberFormat.getNumberInstance //val format = DecimalFormat.getNumberInstance(locale) return format.parse(value).toDouble() } catch (e: Exception) { e.printStackTrace() } //真实情况请勿返回0.0 return 0.0 }... //输出中文格式化字符串 val chineseStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,Locale.CHINA) //中文转中文double val chineseStr_chinese = DoubleNationalUtil.getDouble(chineseStr, Locale.CHINA) //中文转俄文double val chineseStr_russian = DoubleNationalUtil.getDouble(chineseStr, russianLocale) //中文转英语double val chineseStr_english = DoubleNationalUtil.getDouble(chineseStr, Locale.US) //输出俄文格式化字符串 val russianStr = DoubleNationalUtil.getDoubleStr(doubleValue, digits, RoundingMode.DOWN,russianLocale) //俄文转中文double val russianStr_chinese = DoubleNationalUtil.getDouble(russianStr, Locale.CHINA) //俄文转俄文double val russianStr_russian = DoubleNationalUtil.getDouble(russianStr, russianLocale) //俄文转英语double val russianStr_english = DoubleNationalUtil.getDouble(russianStr, Locale.US)</code></pre><p><strong>结果:</strong><br>只有语言相互对应,才能保证输出结果是正确的。<br><img src="https://img-blog.csdn.net/20180815170540319?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h1 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h1><p>涉及到小数或者金额的计算,请使用BigDecimal类。<br>喜欢本篇的朋友,不要忘记点赞哟。</p>]]></content>
<summary type="html">
<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>平常在项目中,经常会遇到String和Double间的转换,或者将Double、Float等类型格式化后显示。当我们的项目是直接在国内使用的
</summary>
</entry>
<entry>
<title>美团Walle使用</title>
<link href="https://blog.ocrnote.com/2018/06/06/mei-tuan-walle-shi-yong/"/>
<id>https://blog.ocrnote.com/2018/06/06/mei-tuan-walle-shi-yong/</id>
<published>2018-06-06T03:37:39.000Z</published>
<updated>2020-01-15T03:18:32.151Z</updated>
<content type="html"><![CDATA[<h1 id="Walle打包"><a href="#Walle打包" class="headerlink" title="Walle打包"></a>Walle打包</h1><h2 id="相关链接"><a href="#相关链接" class="headerlink" title="相关链接"></a>相关链接</h2><p>GitHub:<a href="https://github.com/Meituan-Dianping/walle" target="_blank" rel="noopener">https://github.com/Meituan-Dianping/walle</a><br>个人代码:<a href="https://github.com/linw992/Walle-VasDolly/tree/walle" target="_blank" rel="noopener">https://github.com/linw992/Walle-VasDolly/tree/walle</a> </p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Walle(瓦力):Android Signature V2 Scheme签名下的新一代渠道包打包神器<br>瓦力通过在Apk中的APK Signature Block区块添加自定义的渠道信息来生成渠道包,从而提高了渠道包生成效率,可以作为单机工具来使用,也可以部署在HTTP服务器上来实时处理渠道包Apk的升级网络请求。<br>两种使用方式:<br>Gradle插件方式,方便快速集成<br>命令行方式,最大化满足各种自定义需求</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p><strong>根目录 build.gradle</strong></p><pre><code>buildscript { dependencies { classpath 'com.meituan.android.walle:plugin:1.1.6' }}</code></pre><p><strong>APP目录 build.gradle</strong></p><pre><code>apply plugin: 'walle'dependencies { compile 'com.meituan.android.walle:library:1.1.6'}walle { // 指定渠道包的输出路径 apkOutputFolder = new File("${project.buildDir}/outputs/channels") // 定制渠道包的APK的文件名称 apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' // 渠道配置文件// channelFile = new File("D:/External/WalleApplication/channel")// print("${project.getProjectDir()}") //D:\External\WalleApplication\app// channelFile = new File("${project.getProjectDir()}/channel") // 渠道&额外信息配置文件,与channelFile互斥 都存在时configFile优先执行// configFile = new File("D:/External/WalleApplication/config.json")// print("${project.getProjectDir()}") //D:\External\WalleApplication\app configFile = new File("${project.getProjectDir()}/config.json")}</code></pre><p><strong>apkFileNameFormat:定制渠道包的APK的文件名称, 默认值为’${appName}-${buildType}-${channel}.apk’</strong><br>可使用以下变量:<br> projectName - 项目名字<br> appName - App模块名字<br> packageName - applicationId (App包名packageName)<br> buildType - buildType (release/debug等)<br> channel - channel名称 (对应渠道打包中的渠道名字)<br> versionName - versionName (显示用的版本号)<br> versionCode - versionCode (内部版本号)<br> buildTime - buildTime (编译构建日期时间)<br> fileSHA1 - fileSHA1 (最终APK文件的SHA1哈希值)<br> flavorName - 编译构建 productFlavors 名 </p><h2 id="打包"><a href="#打包" class="headerlink" title="打包"></a>打包</h2><h3 id="1)执行walle-cli命令"><a href="#1)执行walle-cli命令" class="headerlink" title="1)执行walle-cli命令"></a>1)执行walle-cli命令</h3><p>链接:<a href="https://github.com/Meituan-Dianping/walle/blob/master/walle-cli/README.md" target="_blank" rel="noopener">https://github.com/Meituan-Dianping/walle/blob/master/walle-cli/README.md</a><br>查看渠道信息<br>java -jar walle-cli-all.jar show app\build\outputs\channels\app-com.lin.walle-360cn-release-v1.0-1-20180423-105856.apk<br>写入渠道 生成app-release_meituan.apk文件<br>java -jar walle-cli-all.jar put -c meituan app\build\outputs\apk\release\app-release.apk<br>写入渠道和额外信息<br>java -jar walle-cli-all.jar put -c meituan -e buildtime=20161212,hash=xxxxxxx app\build\outputs\apk\release\app-release.apk<br>指定输出文件<br>java -jar walle-cli-all.jar put -c meituan app\build\outputs\apk\release\app-release.apk app\build\outputs\apk\release\app-release111.apk </p><h3 id="2)Gradle配置和命令"><a href="#2)Gradle配置和命令" class="headerlink" title="2)Gradle配置和命令"></a>2)Gradle配置和命令</h3><p>channelFile只支持渠道写入,如果想插入除渠道以外的其他信息,请在walle配置中使用configFile </p><h4 id="1-配置"><a href="#1-配置" class="headerlink" title="1.配置"></a>1.配置</h4><p>channelFile方式: </p><pre><code>walle { // 指定渠道包的输出路径 apkOutputFolder = new File("${project.buildDir}/outputs/channels"); // 定制渠道包的APK的文件名称 apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk'; // 渠道配置文件 channel放在APP下 channelFile = new File("${project.getProjectDir()}/channel") print("${project.getProjectDir()}") //D:\External\WalleApplication\app //channelFile = new File("D:/External/WalleApplication/channel")}</code></pre><p>configFile方式:都存在时configFile优先执行 </p><pre><code>walle { // 指定渠道包的输出路径 apkOutputFolder = new File("${project.buildDir}/outputs/channels"); // 定制渠道包的APK的文件名称 apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk'; // 渠道配置文件 configFile = new File("${project.getProjectDir()}/config.json")}</code></pre><h4 id="2-打包"><a href="#2-打包" class="headerlink" title="2.打包"></a>2.打包</h4><p>全部打包<br>gradlew clean assembleReleaseChannels<br>使用临时configFile config.json生成全部渠道包:<br>gradlew clean assembleReleaseChannels -PconfigFile=D:\External\WalleApplication\config.json<br>gradlew clean assembleReleaseChannels -PchannelFile=D:\External\WalleApplication\config.json<br>生成指定渠道 config.json和channel中的都可以<br>gradlew clean assembleReleaseChannels -PchannelList=meituan<br>支持 productFlavors<br>示例channel_product1<br>gradlew clean assemblechannel_product1ReleaseChannels </p><h2 id="获取渠道信息"><a href="#获取渠道信息" class="headerlink" title="获取渠道信息"></a>获取渠道信息</h2><pre><code>String channel = WalleChannelReader.getChannel(this.getApplicationContext());ChannelInfo channelInfo= WalleChannelReader.getChannelInfo(this.getApplicationContext());if (channelInfo != null) { String channel = channelInfo.getChannel(); Map<String, String> extraInfo = channelInfo.getExtraInfo();}// 或者也可以直接根据key获取String value = WalleChannelReader.get(context, "buildtime");</code></pre><h1 id="加固后重打包"><a href="#加固后重打包" class="headerlink" title="加固后重打包"></a>加固后重打包</h1><p>使用360加固后,会出现渠道信息被擦除的情况。这个时候需要我们重新对加固后的安装包进行重新签名。 </p><h2 id="ProtectedApkResignerForWalle"><a href="#ProtectedApkResignerForWalle" class="headerlink" title="ProtectedApkResignerForWalle"></a>ProtectedApkResignerForWalle</h2><p><strong><a href="https://github.com/Jay-Goo/ProtectedApkResignerForWalle" target="_blank" rel="noopener">官方脚本地址</a></strong> </p><h3 id="用法:"><a href="#用法:" class="headerlink" title="用法:"></a>用法:</h3><ul><li>按照config.py文件中的注释改成自己项目配置</li><li>将已经加固好的包【未签名的包,请不要使用加固客户端签名工具】放到脚本工具根目录下,即app-release.encrypted.apk</li><li>各种渠道的定义是在channel这个文件中,请根据项目情况修改</li><li>运行命令 python ApkResigner.py,即可自动生成所有渠道包。</li></ul><h3 id="支持平台:(需要python环境)"><a href="#支持平台:(需要python环境)" class="headerlink" title="支持平台:(需要python环境)"></a>支持平台:(需要python环境)</h3><p>Windows (Test)<br>Mac OS (Test)<br>Linux<br>注意:python2.x版本正常,python3.x待测试<br><a href="https://www.python.org/ftp/python/" target="_blank" rel="noopener">Python官方下载地址</a></p><h2 id="打包失败"><a href="#打包失败" class="headerlink" title="打包失败"></a>打包失败</h2><ol><li>保证你Android程序的compileSdKVersion 和 buildToosVersion 版本相同</li><li>建议将jdk升级到1.8</li><li>保证自己本地打包签名可以正常运行</li><li>保证配置的相关路径正确,编码格式为UTF-8,不要带异常字符。</li><li>Android SDK buidtools请使用25.0+版本,越新越好。</li><li>检查安装的Python版本<br>检查Python版本是否是Python2.X(由于我的电脑之前安装的是Python 3.6.3,所以没有使用官方推荐的Python 2的环境。) </li><li>检查config.py配置<br>检查config.py中涉及到的文件地址,测试文件地址是否存在。 </li></ol><p>如果上述步骤正确,还是出现打包失败的问题。那么就是ApkResigner.py脚本在我们当前的环境下 <strong>(Windows+Python3)</strong> 不能够正常的运行。<br>执行python ApkResigner.py命令,报错:</p><pre><code>Zip alignment utilityCopyright (C) 2009 The Android Open Source ProjectUsage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip zipalign -c [-p] [-v] <align> infile.zip <align>: alignment in bytes, e.g. '4' provides 32-bit alignment -c: check alignment only (does not modify file) -f: overwrite existing outfile.zip -p: memory page alignment for stored shared object files -v: verbose output -z: recompress using ZopfliUnexpected parameter(s) after input APK (C:\Studio)Error: Unable to access jarfile C:\Studio</code></pre><p><img src="https://img-blog.csdnimg.cn/20190108112033755.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>出现Zip alignment utility,应该是执行对齐命令zipalign 时,语法有问题。<br>为了方便排查出错的原因,我在ApkResigner.py脚本中添加了输出代码。发现是由于路径没有添加双引号,导致执行zipalign、签名、写入命令时出错。<strong>在脚本文件中对路径添加双引号后,便解决。</strong><a href="https://github.com/linw992/Walle-VasDolly/blob/walle/walle-py/ApkResigner.py" target="_blank" rel="noopener">修改后的脚本地址 </a> </p>]]></content>
<summary type="html">
<h1 id="Walle打包"><a href="#Walle打包" class="headerlink" title="Walle打包"></a>Walle打包</h1><h2 id="相关链接"><a href="#相关链接" class="headerlink" titl
</summary>
</entry>
<entry>
<title>Android Studio Template(模板)开发</title>
<link href="https://blog.ocrnote.com/2018/04/19/template-mo-ban-kai-fa/"/>
<id>https://blog.ocrnote.com/2018/04/19/template-mo-ban-kai-fa/</id>
<published>2018-04-19T05:45:33.000Z</published>
<updated>2020-01-15T03:19:52.907Z</updated>
<content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>Android Studio提供的代码模板可帮助我们减少重复编写同一段代码的负担,而且可以遵循优化后的设计和标准。AS采用的是<a href="http://freemarker.org/" target="_blank" rel="noopener">Apache FreeMarker</a>模板引擎。<br>在网上,关于模板开发的资料比较少,而且Studio版本较低,也缺少实际开发中很多功能的示例(比如说Studio在加入kotlin后,我们怎么生成kotlin的模板)。这篇文章将基于TemplateBuilder模板插件,简化模板生成的难度。<br><strong>环境:</strong><br>Android Studio 3.1.1<br>TemplateBuilder 2.0</p><h1 id="一、TemplateBuilder"><a href="#一、TemplateBuilder" class="headerlink" title="一、TemplateBuilder"></a>一、TemplateBuilder</h1><p>简单介绍下TemplateBuilder的使用。</p><h2 id="1-插件安装"><a href="#1-插件安装" class="headerlink" title="1.插件安装"></a>1.插件安装</h2><p>在Studio菜单栏选择【File】-【Settings】,在左侧菜单中选择【Plugins】,点击【Browse repositories】。在弹出框中输入TemplateBuilder,点击安装并重启Studio后生效。<br><img src="https://img-blog.csdn.net/20180419115829829?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h2 id="2-创建模板"><a href="#2-创建模板" class="headerlink" title="2.创建模板"></a>2.创建模板</h2><h4 id="1-定义模板:"><a href="#1-定义模板:" class="headerlink" title="1.定义模板:"></a>1.定义模板:</h4><p>这里定义了activityName、textViewName变量,之后在生成模板时,由用户输入。</p><pre class=" language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> $<span class="token punctuation">{</span>activityName<span class="token punctuation">}</span> <span class="token keyword">extends</span> <span class="token class-name">AppCompatActivity</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> TextView $<span class="token punctuation">{</span>textViewName<span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>Bundle savedInstanceState<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></code></pre><h4 id="2-生成模板"><a href="#2-生成模板" class="headerlink" title="2.生成模板"></a>2.生成模板</h4><p>选中模板文件,按下【ALT + T】(或在Tools下选择Generate Template),配置模板信息<br><img src="https://img-blog.csdn.net/201804191240559?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><h5 id="属性说明:"><a href="#属性说明:" class="headerlink" title="属性说明:"></a>属性说明:</h5><ul><li><strong>Template Category 对应模板的分类,对应选择导入模板时的模板分类,这里默认值是电脑的用户名。</strong></li><li><strong>Template Name 对应模板名称,对应选择导入模板时的模板名称,默认值是当前的Project名称。</strong></li><li><strong>Template Description 对应模板描述信息,对应导入模板时弹出的导入界面的文字描述,默认为空。</strong></li><li><strong>Template Folder 对应生成模板所存放的位置,如果是Mac操作系统则默认为/Applications/Android Studio.app/Contents/plugins/android/lib/templates, Windows系统的话由于差异比较大,就默认为空了,可以自行配置[Android Studio安装目录]/plugins/android/lib/templates(这里只需要配置一次即可,插件将自动保存该位置)。</strong></li><li><strong>Input data 配置模板变量</strong><br>在【Configure Template Data】下点击【add】配置刚才模板中定义的变量,配置完毕后点击【Finish】。<br><img src="https://img-blog.csdn.net/20180419125012700?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br>重启Studio后就可使用该自定义模板。<br><img src="https://img-blog.csdn.net/20180419125226632?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><h5 id="关于Input-data的使用,先解释下每个属性对应的含义"><a href="#关于Input-data的使用,先解释下每个属性对应的含义" class="headerlink" title="关于Input data的使用,先解释下每个属性对应的含义"></a>关于Input data的使用,先解释下每个属性对应的含义</h5></li><li><strong>id 变量名,必须和模板中使用的变量名对应,必填</strong></li><li><strong>name 变量简介,必填</strong></li><li><strong>type 变量类型,string和boolean两种,通过下拉框选择,必填</strong></li><li><strong>default 变量对应的默认值,选填</strong></li><li><strong>help 添加该变量时的提示信息,选填</strong><h1 id="二、定义模板"><a href="#二、定义模板" class="headerlink" title="二、定义模板"></a>二、定义模板</h1>这里我使用TemplateBuilder插件生成mvp的模板。通过修改相关的属性,实现Activity和layout动态生成,并在AndroidManifest.xml中注册。最后添加kotlin的模板配置,这样模板最终完成。<h2 id="1-创建Java模板"><a href="#1-创建Java模板" class="headerlink" title="1.创建Java模板"></a>1.创建Java模板</h2>首先创建一个MVP的Activity,将动态的属性声明为变量。<pre class=" language-java"><code class="language-java"><span class="token keyword">package</span> $<span class="token punctuation">{</span>packageName<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></li></ul><p>import android.os.Bundle;<br>import com.blackbox.medicalpension.common.mvp.base.BaseCommonActivity;<br>import com.blackbox.medicalpension.common.mvp.mvp.BasePresenter;</p><p>import org.jetbrains.annotations.Nullable;</p><p>import java.util.List;</p><p>public class ${activityClass} extends BaseCommonActivity{</p><pre><code>@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.${layoutName}); initTitleWithBack("${titleName}");}@Nullable@Overridepublic List<BasePresenter<?>> createPresenter() { return null;}</code></pre><p>}</p><pre><code>创建对应的Layout布局文件</code></pre><?xml version="1.0" encoding="utf-8"?><p><android.support.constraint.ConstraintLayout xmlns:android=”<a href="http://schemas.android.com/apk/res/android"" target="_blank" rel="noopener">http://schemas.android.com/apk/res/android"</a><br> xmlns:app=”<a href="http://schemas.android.com/apk/res-auto"" target="_blank" rel="noopener">http://schemas.android.com/apk/res-auto"</a><br> xmlns:tools=”<a href="http://schemas.android.com/tools"" target="_blank" rel="noopener">http://schemas.android.com/tools"</a><br> android:layout_width=”match_parent”<br> android:layout_height=”match_parent”<br> android:background=”@color/white”></p><pre><code><include android:id="@+id/include_layout" layout="@layout/lin_title" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /></code></pre><p></android.support.constraint.ConstraintLayout></p><pre><code>## 2.模板生成选中两个模板文件,点击【ALT + T】,弹出【Configure Template Data】窗口。定义activityClass、layoutName、titleName三个变量,并【Finish】,生成模板。![这里写图片描述](https://img-blog.csdn.net/20180419130815864?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)模板文件存放在...\Android Studio\plugins\android\lib\templates下![这里写图片描述](https://img-blog.csdn.net/20180419131020722?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)模板文件后缀名都是以【.ftl】结尾。 - globals.xml.ftl 全局变量文件 存放的是一些全局变量 - recipe.xml.ftl 配置要引用的模板路径以及生成文件的路径 - template.xml 模板的配置信息,以及要输入的参数.定义了模板的流程框架 基本结构 - template_blank_activity.png 显示的缩略图(只是展示用) - LinActivity.java.ftl Activity模板文件#### 1.修改配置进入到...\Android Studio\plugins\android\lib\templates\linw9目录下##### 1.修改template.xml文件为方便用户输入Activity名称或Layout名称,自动提示Layout或Activity名称,需要在文件中添加两个属性:constraints="class|unique|nonempty"suggest="\${layoutToActivity(layoutName)}"suggest="\${activityToLayout(activityClass)}"</code></pre><?xml version="1.0"?><p><template format="5" revision="5" name="LinMvpActivity" minApi="7" minBuildApi="14" description="Mvp Activity"></p><pre><code><category value="linw9" /><formfactor value="Mobile" /><!-- input data --><parameter id="activityClass" name="Activity Name" type="string" constraints="class|unique|nonempty" suggest="${layoutToActivity(layoutName)}" default="MvpActivity" help="" /><parameter id="layoutName" name="Layout Name" type="string" constraints="layout|unique|nonempty" suggest="${activityToLayout(activityClass)}" default="activity_mvp" help="" /><parameter id="titleName" name="Title Name" type="string" default="Title" help="" /><!-- 128x128 thumbnails relative to com.puke.template.xml --><thumbs> <!-- default thumbnail is required --> <thumb>template_cover.png</thumb></thumbs><globals file="globals.xml.ftl" /><execute file="recipe.xml.ftl" /></code></pre></template>```##### 2.修改AndroidManifest.xml文件进入到...\Android Studio\plugins\android\lib\templates\linw9\LinMvpActivity\root 目录下,打开AndroidManifest.xml.ftl文件,修改内容让Studio自动注册Activity。需要注意的是:需要对AndroidManifest进行代码格式化(Format),不然使用的时候Studio可能会提示Merge。只针对Java代码的模板到此结束,重新启动Studio后即可使用该模板。<pre><code><?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="${packageName}"> <application> <activity android:name="${packageName}.${activityClass}"> </activity> </application></manifest></code></pre><h5 id="3-增加Kotlin模板"><a href="#3-增加Kotlin模板" class="headerlink" title="3.增加Kotlin模板"></a>3.增加Kotlin模板</h5><p>在网上,我没有查找到关于Kotlin模板配置的相关文章,这里给大家讲解下我的方法。<br>我是通过学习Studio自带的模板,从中找到了配置Kotlin模板的方法,仅供大家参考。</p><h6 id="1-创建Kotlin模板"><a href="#1-创建Kotlin模板" class="headerlink" title="1.创建Kotlin模板"></a>1.创建Kotlin模板</h6><p>进入到…\Android Studio\plugins\android\lib\templates\linw9\LinMvpActivity\root\src\app_package目录下,直接创建kotlin模板</p><pre><code>package ${packageName}import android.os.Bundleimport com.blackbox.medicalpension.common.mvp.base.BaseCommonActivityimport com.blackbox.medicalpension.common.mvp.mvp.BasePresenterimport kotlinx.android.synthetic.main.${layoutName}.*class ${activityClass} : BaseCommonActivity(){ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.${layoutName}) initTitleWithBack("${titleName}") } override fun createPresenter(): MutableList<BasePresenter<*>> ?{ return null }}</code></pre><h6 id="2-修改recipe-xml-ftl"><a href="#2-修改recipe-xml-ftl" class="headerlink" title="2.修改recipe.xml.ftl"></a>2.修改recipe.xml.ftl</h6><p>进入到…\Android Studio\plugins\android\lib\templates\linw9\LinMvpActivity目录下,打开recipe.xml.ftl<br>将java改为${ktOrJavaExt}变量。</p><pre><code><?xml version="1.0"?><recipe> <instantiate from="root/src/app_package/LinActivity.${ktOrJavaExt}.ftl" to="${escapeXmlAttribute(srcOut)}/${activityClass}.${ktOrJavaExt}" /> <instantiate from="root/res/layout/activity_lin.xml.ftl" to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" /> <merge from="root/AndroidManifest.xml.ftl" to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" /></recipe></code></pre><h6 id="3-修改globals-xml-ftl"><a href="#3-修改globals-xml-ftl" class="headerlink" title="3.修改globals.xml.ftl"></a>3.修改globals.xml.ftl</h6><p>进入到…\Android Studio\plugins\android\lib\templates\linw9\LinMvpActivity目录下,打开globals.xml.ftl<br>添加ktOrJavaExt变量声明:<br><img src="https://img-blog.csdn.net/20180419134151226?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA5ODcwMzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p><pre><code><?xml version="1.0"?><globals> <#assign generateKotlin=(((includeKotlinSupport!false) || (language!'Java')?string == 'Kotlin'))> <global id="generateKotlin" type="boolean" value="${generateKotlin?string}" /> <global id="ktOrJavaExt" type="string" value="${generateKotlin?string('kt','java')}" /> <global id="resOut" value="${resDir}" /> <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" /> <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" /></globals></code></pre><h6 id="4-结束"><a href="#4-结束" class="headerlink" title="4.结束"></a>4.结束</h6><p>重启Studio后,就可以生成Java或Kotlin的代码。对于写的不好的地方,希望大家多提建议。</p><h1 id="三、其它"><a href="#三、其它" class="headerlink" title="三、其它"></a>三、其它</h1><p>源码地址:<a href="https://download.csdn.net/download/u010987039/10360074" target="_blank" rel="noopener">https://download.csdn.net/download/u010987039/10360074</a><br>码云地址:<a href="https://gitee.com/personlin/emplate_java_kotlin" target="_blank" rel="noopener">https://gitee.com/personlin/emplate_java_kotlin</a><br>GitHub地址:<a href="https://github.com/linw992/Template" target="_blank" rel="noopener">https://github.com/linw992/Template</a></p>]]></content>
<summary type="html">
<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>Android Studio提供的代码模板可帮助我们减少重复编写同一段代码的负担,而且可以遵循优化后的设计和标准。AS采用的是<a href
</summary>
</entry>
<entry>
<title>'解决EditText右对齐兼容问题,一个属性搞定'</title>
<link href="https://blog.ocrnote.com/2017/11/27/jie-jue-edittext-you-dui-qi-jian-rong-wen-ti-yi-ge-shu-xing-gao-ding/"/>
<id>https://blog.ocrnote.com/2017/11/27/jie-jue-edittext-you-dui-qi-jian-rong-wen-ti-yi-ge-shu-xing-gao-ding/</id>
<published>2017-11-27T05:39:15.000Z</published>
<updated>2020-01-15T03:20:53.726Z</updated>
<content type="html"><![CDATA[<h2 id="1-问题描述"><a href="#1-问题描述" class="headerlink" title="1.问题描述"></a>1.问题描述</h2><p>在使用EditText时,根据产品设计,某些地方需要让文字右对齐显示,光标默认也显示在最右边。这个时候我们自然而然想到设置android:gravity=”right” 这个属性,运行在模拟器跟自己的手机上都没出现问题。然后测试拿着手机测试,突然跟你说这个地方有问题,当你看到错位后的界面一脸懵逼。这尼M是怎么出现的?</p><p><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcxMTI3MTI0ODEyMDQ0?x-oss-process=image/format,png" alt=""></p><h2 id="2-解决方法"><a href="#2-解决方法" class="headerlink" title="2.解决方法"></a>2.解决方法</h2><p>百度了下,找到两个解决办法:</p><h4 id="1-在布局中添加android-textCursorDrawable-”-null”–-gt-部分手机有效"><a href="#1-在布局中添加android-textCursorDrawable-”-null”–-gt-部分手机有效" class="headerlink" title="1. 在布局中添加android:textCursorDrawable=”@null”–>部分手机有效"></a>1. 在布局中添加android:textCursorDrawable=”@null”–>部分手机有效</h4><h4 id="2-TextView和EditText共用,控制光标位置-–-gt-这也太麻烦了,有没得更好的方法"><a href="#2-TextView和EditText共用,控制光标位置-–-gt-这也太麻烦了,有没得更好的方法" class="headerlink" title="2. TextView和EditText共用,控制光标位置 –>这也太麻烦了,有没得更好的方法"></a>2. TextView和EditText共用,控制光标位置 –>这也太麻烦了,有没得更好的方法</h4><p> 既然百度暂时解决不了这个问题,那去官网看看相应的API吧。哈哈,找到两个属性可能会有用。<br> android:textDirection=”rtl” ,试了下不行<br> android:layoutDirection=”rtl” ,居然可以,问题完美解决<br> <img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcxMTI3MTMzNzMxOTA5?x-oss-process=image/format,png" alt=""><br> <img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcxMTI3MTMzNzU0NTYz?x-oss-process=image/format,png" alt=""></p><h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><p>下载地址:<a href="http://download.csdn.net/download/u010987039/10134195" target="_blank" rel="noopener">http://download.csdn.net/download/u010987039/10134195</a><br>如果解决了大家的问题,记得多评论啊,让更多的人看到。</p>]]></content>
<summary type="html">
<h2 id="1-问题描述"><a href="#1-问题描述" class="headerlink" title="1.问题描述"></a>1.问题描述</h2><p>在使用EditText时,根据产品设计,某些地方需要让文字右对齐显示,光标默认也显示在最右边。这个时候我们自
</summary>
</entry>
<entry>
<title>Android集成Cordova与Crosswalk'</title>
<link href="https://blog.ocrnote.com/2017/04/20/android-ji-cheng-cordova-yu-crosswalk/"/>
<id>https://blog.ocrnote.com/2017/04/20/android-ji-cheng-cordova-yu-crosswalk/</id>
<published>2017-04-20T07:39:31.000Z</published>
<updated>2020-01-15T03:21:55.790Z</updated>
<content type="html"><![CDATA[<p>##1. 介绍<br>###1.1 Cordova<br>Cordova是贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码,是驱动PhoneGap的核心引擎。Adobe将会继续以Cordova加上PhoneGap Build和Adobe Shadow的组合提供PhoneGap。<br>特性:<br> ● 移动应用程序使用Html、Css、JS<br> ● Cordova提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头、麦克风等。<br> ● 一次开发多个平台<br> ● 开源免费<br>###1.2 Node.js<br> Node.js是基于Chrome的V8 JavaScript引擎的JavaScript运行时环境。V8引 擎执行Javascript的速度非常快,性能非常好。Node.js对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。Node.js使用事件驱动的非阻塞I / O模型,使其轻量级和高效。Node.js的包生态系统,npm,是世界上最大的开源库的生态系统。<br>###1.3 Crosswalk<br> Crosswalk 作为一款开源的 web 引擎,正是为了跨越这些障碍而生。目前 Crosswalk 正式支<br>持的移动操作系统包括 Android 和 Tizen,在 Android 4.0 及以上的系统中使用 Crosswalk 的<br>Web 应用程序在 HTML5 方面可以有一致的体验,同时和系统的整合交互方面(比如启动画<br>面、权限管理、应用切换、社交分享等等)可以做到类似原生应用。<br>##2. 开发环境及搭建<br>###2.1 Node.js、Cordova安装<br>首先去Node.js的<a href="https://nodejs.org/en/" target="_blank" rel="noopener">官网</a>下载并安装,然后打开命令行输入 npm install -g cordova 安装Cordova<br>###2.2 创建项目<br>打开命令行执行:</p><pre><code>cordova create Android6.2.1 com.blog.test CordovaTest</code></pre><p>创建一个项目 cordova create 【文件名】【包名】【项目名】</p><pre><code>cordova platform add android@6.2.1</code></pre><p>安装Android平台6.2.1版本<br>Cordova Android版本已于2017年4月05日更新到了6.2.1版本,建议大家使用该版本。<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTYwMjM2NjY0?x-oss-process=image/format,png" alt=""><br>###2.3 编译运行<br>####2.3.1 编译<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTYxOTQ0MjE5?x-oss-process=image/format,png" alt=""><br>当遇到上面提示的问题时,需要在环境变量中设置gradle的文件位置。<br><strong>如果已安装Android Studio:</strong><br>在【系统变量】栏新建一个变量GRADLE_HOME 变量值是gradle的文件位置<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTYyNzI5NDQ5?x-oss-process=image/format,png" alt=""><br>在系统变量【Path】中添加变量值 “;%GRADLE_HOME%\bin”<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTYyODAzNjc4?x-oss-process=image/format,png" alt=""><br>最后在命令行中输入gradle -version,检查是否配置完毕<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTYyODQzNTQ0?x-oss-process=image/format,png" alt=""><br><strong>如果没有安装Android Studio的:</strong><br>在Gradle<a href="https://gradle.org/" target="_blank" rel="noopener">官网</a>下载一个版本并解压,之后按照上述内容配置环境变量即可。<a href="http://download.csdn.net/detail/u010987039/9734779" target="_blank" rel="noopener">Gradle3.3下载</a><br>####2.3.2 运行</p><pre><code>使用真机运行cordova run anroid使用模拟器运行 cordova emulate android使用网页打开cordova serve</code></pre><p>###2.4 插件相关</p><pre><code>cordova plugin ls显示已安装的插件cordova plugin add cordova-plugin-crosswalk-webview安装Crosswalk插件cordova plugin rm cordova-plugin-camera删除插件cordova plugin update更新所有插件</code></pre><p>##3. Cordova使用<br>###3.1 config.xml文件加载<br><strong>config.xml主要标签介绍 (具体请见<a href="http://cordova.apache.org/docs/en/6.x/config_ref/index.html)" target="_blank" rel="noopener">http://cordova.apache.org/docs/en/6.x/config_ref/index.html)</a></strong><br>widget:id填写app所有人的域名,version填写app的版本号<br>name:app名称<br>description:app描述,会在app stroe里显示<br>author:app作者相关信息,会在app stroe里显示<br>content:指定app开始指向页面<br>access:指定app可进行通信的域名,*为所有<br>preference:偏好设置,可针对不同平台进行个性化设置。<br>如下图所示,ConfigXmlParser负责解析config.xml文件,然后将preference标签下的内容拿给CordovaPreference。<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTcwNjM3NDg4?x-oss-process=image/format,png" alt=""><br>###3.2 CordovaWebView赋值、初始化<br><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTcwNDIwMTcwNzAzNzQ1?x-oss-process=image/format,png" alt=""><br>###3.3 显示网页<br>####3.3.1 方式一:直接继承CordovaActivity(官网推荐)</p><pre><code>public class VideoActivity extends CordovaActivity { @BindView(R.id.video) FrameLayout video_layout; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); ButterKnife.bind(this); loadUrl("https://www.baidu.com/"); } @Override protected void createViews() { FrameLayout.LayoutParams params=new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); appView.getView().setLayoutParams(params); video_layout.addView(appView.getView()); appView.getView().requestFocusFromTouch(); }}</code></pre><p>####3.3.2 方式二:自定义</p><pre><code>public class VideoActivity extends BaseActivity { @BindView(R.id.video) FrameLayout video_layout; private CordovaWebView webView; private CordovaPreferences preferences; private List<PluginEntry> pluginEntries; private CordovaInterfaceImpl cordovaInterface; private boolean keepRunning=true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); init(); cordovaInterface=makeCordovaInterface(); if (savedInstanceState != null) { cordovaInterface.restoreInstanceState(savedInstanceState); } loadUrl("https://www.baidu.com/"); } private void init(){ ConfigXmlParser parser = new ConfigXmlParser(); parser.parse(this); preferences = parser.getPreferences(); pluginEntries=parser.getPluginEntries(); } private void initWebView(){ webView=new CordovaWebViewImpl(new XWalkWebViewEngine(this,preferences)); FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); webView.getView().setLayoutParams(params); video_layout.addView(webView.getView()); if (preferences.contains("BackgroundColor")) { try { int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK); // Background of activity: webView.getView().setBackgroundColor(backgroundColor); } catch (NumberFormatException e){ e.printStackTrace(); } } webView.getView().requestFocusFromTouch(); if(!webView.isInitialized()){ webView.init(cordovaInterface,pluginEntries,preferences); } cordovaInterface.onCordovaInit(webView.getPluginManager()); //音量控制 // Wire the hardware volume controls to control media if desired. String volumePref = preferences.getString("DefaultVolumeStream", ""); if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) { setVolumeControlStream(AudioManager.STREAM_MUSIC); } } protected void loadUrl(String url){ if(webView==null){ initWebView(); } webView.loadUrlIntoView(url,true); } //对照CordovaActivity中CordovaInterfaceImpl的实现及Activity的生命周期 ....}</code></pre>]]></content>
<summary type="html">
<p>##1. 介绍<br>###1.1 Cordova<br>Cordova是贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码,是驱动PhoneGap的核心引擎。Adobe将会继续以Cordova加上PhoneGap Build和Adobe Shadow的组
</summary>
</entry>
<entry>
<title>'ijkplayer播放器详解使用教程'</title>
<link href="https://blog.ocrnote.com/2016/08/24/ijkplayer-bo-fang-qi-xiang-jie-shi-yong-jiao-cheng/"/>
<id>https://blog.ocrnote.com/2016/08/24/ijkplayer-bo-fang-qi-xiang-jie-shi-yong-jiao-cheng/</id>
<published>2016-08-24T07:42:30.000Z</published>
<updated>2020-01-15T03:22:58.815Z</updated>
<content type="html"><![CDATA[<h2 id="1-认识ijkplayer"><a href="#1-认识ijkplayer" class="headerlink" title="1.认识ijkplayer"></a>1.认识ijkplayer</h2><p>最近公司准备开发一款视频播放及直播的应用,找了许多开源的框架,大部分都是基于ffmpeg开发的。最开始准备用Vitamio框架开发的,相关的文章也比较丰富,结果对于非个人移动应用均需购买Vitamio使用授权。不过B站开源的ijkplayer也不错,而且也不需要商业授权。<br>ijkplayer是一个基于FFmpeg的轻量级Android/iOS视频播放器。FFmpeg的是全球领先的多媒体框架,能够解码,编码, 转码,复用,解复用,流,过滤器和播放大部分的视频格式。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。</p><h2 id="2-环境配置"><a href="#2-环境配置" class="headerlink" title="2.环境配置"></a>2.环境配置</h2><p>项目中引入ijkplayer环境有两种方式。</p><h4 id="2-1在Gradle中引入"><a href="#2-1在Gradle中引入" class="headerlink" title="2.1在Gradle中引入"></a><strong>2.1在Gradle中引入</strong></h4><pre><code># requiredallprojects { repositories { jcenter() }}dependencies { # required, enough for most devices. compile 'tv.danmaku.ijk.media:ijkplayer-java:0.6.1' compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.6.1' # Other ABIs: optional compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.6.1' compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.6.1' compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.6.1' compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.6.1' # ExoPlayer as IMediaPlayer: optional, experimental compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.6.1'}</code></pre><h4 id="2-2在Ubuntu下编译源码得到"><a href="#2-2在Ubuntu下编译源码得到" class="headerlink" title="2.2在Ubuntu下编译源码得到"></a><strong>2.2在Ubuntu下编译源码得到</strong></h4><p><strong>Ubuntu需要安装homebrew, git, yasm</strong></p><pre><code># install homebrew, git, yasmruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"brew install gitbrew install yasm# add these lines to your ~/.bash_profile or ~/.profile# export ANDROID_SDK=<your sdk path># export ANDROID_NDK=<your ndk path># on Cygwin (unmaintained)# install git, make, yasm</code></pre><p><strong>开始编译</strong></p><pre><code>git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-androidcd ijkplayer-androidgit checkout -B latest k0.6.1./init-android.shcd android/contrib./compile-ffmpeg.sh clean./compile-ffmpeg.sh allcd .../compile-ijk.sh all# Android Studio:# Open an existing Android Studio project# Select android/ijkplayer/ and import## define ext block in your root build.gradle# ext {# compileSdkVersion = 23 // depending on your sdk version# buildToolsVersion = "23.0.0" // depending on your build tools version## targetSdkVersion = 23 // depending on your sdk version# }## Eclipse: (obselete)# File -> New -> Project -> Android Project from Existing Code# Select android/ and import all project# Import appcompat-v7# Import preference-v7## Gradle# cd ijkplayer# gradle</code></pre><p><strong>目录结构</strong></p><p><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYwODI0MTc1MjEyOTg5?x-oss-process=image/format,png" alt="目录结构"></p><h2 id="3-播放器使用"><a href="#3-播放器使用" class="headerlink" title="3.播放器使用"></a>3.播放器使用</h2><p>可能是网络的问题,使用Gradle导入会花费很长时间,如果遇到超时,还得重头来一遍,太费时间了。后来我就直接在Ubuntu下编译后,在Android Studio下导入该项目。我先介绍下Demo中利用ijkplayer播放视频的过程。</p><h4 id="3-1初始化播放器"><a href="#3-1初始化播放器" class="headerlink" title="3.1初始化播放器"></a><strong>3.1初始化播放器</strong></h4><pre><code>IjkMediaPlayer.loadLibrariesOnce(null);IjkMediaPlayer.native_profileBegin("libijkplayer.so");</code></pre><h4 id="3-2初始化IjkVideoView"><a href="#3-2初始化IjkVideoView" class="headerlink" title="3.2初始化IjkVideoView"></a><strong>3.2初始化IjkVideoView</strong></h4><pre><code>//这里使用的是Demo中提供的AndroidMediaController类控制播放相关操作mMediaController = new AndroidMediaController(this, false);mMediaController.setSupportActionBar(actionBar);mVideoView = (IjkVideoView) findViewById(R.id.video_view);mVideoView.setMediaController(mMediaController);</code></pre><h4 id="3-3设置本地视频文件位置或服务器地址,然后播放"><a href="#3-3设置本地视频文件位置或服务器地址,然后播放" class="headerlink" title="3.3设置本地视频文件位置或服务器地址,然后播放"></a><strong>3.3设置本地视频文件位置或服务器地址,然后播放</strong></h4><pre><code>mVideoView.setVideoPath(mVideoPath);mVideoView.start();</code></pre><h4 id="3-4Activity销毁时,需要释放资源"><a href="#3-4Activity销毁时,需要释放资源" class="headerlink" title="3.4Activity销毁时,需要释放资源"></a><strong>3.4Activity销毁时,需要释放资源</strong></h4><pre><code>@Overridepublic void onBackPressed() { mBackPressed = true; super.onBackPressed();}@Overrideprotected void onStop() { super.onStop(); //点击返回或不允许后台播放时 释放资源 if (mBackPressed || !mVideoView.isBackgroundPlayEnabled()) { mVideoView.stopPlayback(); mVideoView.release(true); mVideoView.stopBackgroundPlay(); } else { mVideoView.enterBackground(); } IjkMediaPlayer.native_profileEnd();}</code></pre><h2 id="4-自定义播放器"><a href="#4-自定义播放器" class="headerlink" title="4.自定义播放器"></a>4.自定义播放器</h2><p>当然官方提供的Demo只是演示视频播放的基本操作,对于视频播放的控制、全屏等操作,还要自己动手做。</p><h4 id="4-1部分声明"><a href="#4-1部分声明" class="headerlink" title="4.1部分声明"></a><strong>4.1部分声明</strong></h4><pre><code>private static final int SIZE_DEFAULT = 0;private static final int SIZE_4_3 = 1;private static final int SIZE_16_9 = 2;private int currentSize = SIZE_16_9;private IjkVideoView video;private SeekBar seekBar;</code></pre><h4 id="4-2视频播放比例"><a href="#4-2视频播放比例" class="headerlink" title="4.2视频播放比例"></a><strong>4.2视频播放比例</strong></h4><p>这里需要修改IjkVideoView部分代码后,才支持按比例播放</p><pre><code>//修改相关代码private static final int[] s_allAspectRatio = { IRenderView.AR_ASPECT_FIT_PARENT, IRenderView.AR_ASPECT_FILL_PARENT, IRenderView.AR_ASPECT_WRAP_CONTENT, IRenderView.AR_MATCH_PARENT, IRenderView.AR_16_9_FIT_PARENT, IRenderView.AR_4_3_FIT_PARENT};private int mCurrentAspectRatioIndex = 3;//0private int mCurrentAspectRatio = s_allAspectRatio[3];//0private int mCurrentRender = RENDER_TEXTURE_VIEW;//增加下面方法public IRenderView getmRenderView() { return mRenderView;}public int getmVideoWidth() { return mVideoWidth;}public int getmVideoHeight() { return mVideoHeight;}</code></pre><p>设置视频播放比例</p><pre><code>public void setScreenRate(int rate) { int width = 0; int height = 0; if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {// 横屏 if (rate == SIZE_DEFAULT) { width = video.getmVideoWidth(); height = video.getmVideoHeight(); } else if (rate == SIZE_4_3) { width = screenHeight / 3 * 4; height = screenHeight; } else if (rate == SIZE_16_9) { width = screenHeight / 9 * 16; height = screenHeight; } } else { //竖屏 if (rate == SIZE_DEFAULT) { width = video.getmVideoWidth(); height = video.getmVideoHeight(); } else if (rate == SIZE_4_3) { width = screenWidth; height = screenWidth * 3 / 4; } else if (rate == SIZE_16_9) { width = screenWidth; height = screenWidth * 9 / 16; } } if (width > 0 && height > 0) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) video.getmRenderView().getView().getLayoutParams(); lp.width = width; lp.height = height; video.getmRenderView().getView().setLayoutParams(lp); }}</code></pre><h4 id="4-3屏幕方向切换"><a href="#4-3屏幕方向切换" class="headerlink" title="4.3屏幕方向切换"></a><strong>4.3屏幕方向切换</strong></h4><pre><code>private void fullChangeScreen() { if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {// 切换为竖屏 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }}</code></pre><h4 id="4-4全屏播放"><a href="#4-4全屏播放" class="headerlink" title="4.4全屏播放"></a><strong>4.4全屏播放</strong></h4><pre><code>@Overridepublic void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //重新获取屏幕宽高 initScreenInfo(); if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {//切换为横屏 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) video.getLayoutParams(); lp.height = screenHeight; lp.width = screenWidth; video.setLayoutParams(lp); } else { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) video.getLayoutParams(); lp.height = screenWidth * 9 / 16; lp.width = screenWidth; video.setLayoutParams(lp); } setScreenRate(currentSize);}</code></pre><h4 id="4-5播放进度"><a href="#4-5播放进度" class="headerlink" title="4.5播放进度"></a><strong>4.5播放进度</strong></h4><pre><code>seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { .... @Override public void onStopTrackingTouch(SeekBar seekBar) { video.seekTo(seekBar.getProgress()*video.getDuration()/100); ... }});//视频开始播放时使用handle.sendMessageDelayed更新时间显示private void refreshTime(){ int totalSeconds = video.getCurrentPosition() / 1000; int seconds = totalSeconds % 60; int minutes = (totalSeconds / 60) % 60; int hours = totalSeconds / 3600; String ti=hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds):String.format("%02d:%02d", minutes, seconds); time.setText(ti);}</code></pre><h2 id="5-相关资料"><a href="#5-相关资料" class="headerlink" title="5.相关资料"></a>5.相关资料</h2><ul><li>官网地址:<a href="https://github.com/Bilibili/ijkplayer" target="_blank" rel="noopener">https://github.com/Bilibili/ijkplayer</a></li><li>已编译好的环境:<a href="http://download.csdn.net/detail/u010987039/9611675" target="_blank" rel="noopener">http://download.csdn.net/detail/u010987039/9611675</a></li><li>测试用服务器地址:<br><a href="http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8(可用)" target="_blank" rel="noopener">http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8(可用)</a><br><a href="http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8" target="_blank" rel="noopener">http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8</a><br><a href="http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8" target="_blank" rel="noopener">http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8</a><br><a href="http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8" target="_blank" rel="noopener">http://playertest.longtailvideo.com/adaptive/oceans_aes/oceans_aes.m3u8</a> (AES encrypted)<br><a href="http://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8" target="_blank" rel="noopener">http://playertest.longtailvideo.com/adaptive/captions/playlist.m3u8</a> (HLS stream with CEA-608 captions)<br><a href="http://playertest.longtailvideo.com/adaptive/wowzaid3/playlist.m3u8" target="_blank" rel="noopener">http://playertest.longtailvideo.com/adaptive/wowzaid3/playlist.m3u8</a> (with metadata)<br><a href="http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8" target="_blank" rel="noopener">http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8</a><br><a href="http://cdn-fms.rbs.com.br/hls-vod/sample1_1500kbps.f4v.m3u8" target="_blank" rel="noopener">http://cdn-fms.rbs.com.br/hls-vod/sample1_1500kbps.f4v.m3u8</a><br><a href="http://cdn-fms.rbs.com.br/vod/hls_sample1_manifest.m3u8(可用)" target="_blank" rel="noopener">http://cdn-fms.rbs.com.br/vod/hls_sample1_manifest.m3u8(可用)</a><br><a href="http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch1/appleman.m3u8" target="_blank" rel="noopener">http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch1/appleman.m3u8</a> (LIVE TV可用)<br><a href="http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch2/appleman.m3u8" target="_blank" rel="noopener">http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch2/appleman.m3u8</a> (LIVE TV)<br><a href="http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch3/appleman.m3u8" target="_blank" rel="noopener">http://vevoplaylist-live.hls.adaptive.level3.net/vevo/ch3/appleman.m3u8</a> (LIVE TV)<br><a href="https://dl.dropboxusercontent.com/u/7303267/website/m3u8/index.m3u8" target="_blank" rel="noopener">https://dl.dropboxusercontent.com/u/7303267/website/m3u8/index.m3u8</a> (VOD) - [updated]<br><a href="http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8" target="_blank" rel="noopener">http://content.jwplatform.com/manifests/vM7nH0Kl.m3u8</a> (link protection, video not encrypted)<br><a href="http://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8" target="_blank" rel="noopener">http://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8</a> (4K)<br><a href="http://cdn3.viblast.com/streams/hls/airshow/playlist.m3u8" target="_blank" rel="noopener">http://cdn3.viblast.com/streams/hls/airshow/playlist.m3u8</a> (4K)</li></ul><h2 id="6-2018-07-26更新"><a href="#6-2018-07-26更新" class="headerlink" title="6.2018-07-26更新"></a>6.2018-07-26更新</h2><p>方便大家使用,提供编译好的各平台so文件,再引入“ijkplayer-java”就可以直接使用。<br><a href="http://download.csdn.net/detail/u010987039/9800324" target="_blank" rel="noopener">http://download.csdn.net/detail/u010987039/9800324</a><br>百度网盘下载地址:<a href="https://pan.baidu.com/s/1eSst88U" target="_blank" rel="noopener">https://pan.baidu.com/s/1eSst88U</a><br>QQ:631344199</p>]]></content>
<summary type="html">
<h2 id="1-认识ijkplayer"><a href="#1-认识ijkplayer" class="headerlink" title="1.认识ijkplayer"></a>1.认识ijkplayer</h2><p>最近公司准备开发一款视频播放及直播的应用,找了许多开
</summary>
</entry>
</feed>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。