首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

【Android开发经验】LayoutInflater—— 你可能对它并不了解甚至错误使用

今天,看到了一篇文章讲LayoutInflater的使用方法。瞬间感觉自己对这个类确实不够了解,于是简单的看了下LayoutInflater类的源码。对这个类有了新的认识。 首先。LayoutInflater这个类是用来干嘛的呢? 我们最经常使用的便是LayoutInflater的inflate方法。这种方法重载了四种调用方式。分别为: 1.public View inflate(int resource, ViewGroup root) 2.public View inflate(int resource, ViewGroup root, boolean attachToRoot) 3.public View inflate(XmlPullParser parser, ViewGroup root) 4.public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) 这四种使用方式中。我们最经常使用的是第一种方式。inflate方法的主要作用就是将xml转换成一个View对象。用于动态的创建布局。尽管重载了四个方法。可是这四种方法终于调用的,还是第四种方式。 第四种方式也非常好理解,内部实现原理就是利用Pull解析器。对Xml文件进行解析,然后返回View对象。 我们以我们经常使用的第一种形式为例,你在重写BaseAdapter的getView方法的时候是否这样做过 public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflate(R.layout.item_row, null); } return convertView; } inflate方法有三个參数。各自是 1.resource布局的资源id 2.root填充的根视图 3.attachToRoot是否将载入的视图绑定到根视图中 在这个样例中,我们将root參数设为空,功能确实实现了。可是这里还隐藏着一个隐患。这样的方式并非inflate正确的使用姿势,以下我们通过一个Demo,来说一下这样使用造成的弊端。 首先,我们建立一个这样的项目 这里三个界面,一个主界面,两个測试界面,布局文件里。主界面仅仅负责界面跳转,两个測试界面都是一个简单的Listview,item布局显示效果例如以下 相应的布局文件例如以下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp" android:background="@android:color/holo_orange_light" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="11" android:textColor="@android:color/black" android:textSize="22sp" /> </LinearLayout> OneActivity的代码例如以下 public class OneActivity extends Activity { private ListView list1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_one); list1 = (ListView) findViewById(R.id.list1); list1.setAdapter(new MyAdapter(this)); } private class MyAdapter extends BaseAdapter { private LayoutInflater inflater; MyAdapter(Context context) { inflater = LayoutInflater.from(context); } @Override public int getCount() { return 20; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.item_list, null); } TextView tv = (TextView) convertView.findViewById(R.id.tv); tv.setText(position+""); return convertView; } } } TwoActivity的代码例如以下 public class TwoActivity extends Activity { private ListView list2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_two); list2 = (ListView) findViewById(R.id.list2); list2.setAdapter(new MyAdapter(this)); } private class MyAdapter extends BaseAdapter { private LayoutInflater inflater; MyAdapter(Context context) { inflater = LayoutInflater.from(context); } @Override public int getCount() { return 20; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.item_list, parent,false); } TextView tv = (TextView) convertView.findViewById(R.id.tv); tv.setText(position + ""); return convertView; } } } 两个文件最关键的差别就一句话。 在getView方法中,OneActivity是 convertView = inflater.inflate(R.layout.item_list, null); 在getView方法中,TwoActivity是 convertView = inflater.inflate(R.layout.item_list, parent,false); 我们先看一下显示效果。再说两者的差别 OneActivity效果 TwoActivity的显示效果 我们能够非常明显的看出来,使用第一种方式,根布局的高度设置60dp没有起作用,系统还是依照包裹内容的方式载入的,为什么会产生这样的效果呢?我们从须要inflate方法的源码中找一下答案。 首先,方式一的源码实现 public View inflate(XmlPullParser parser, ViewGroup root) { return inflate(parser, root, root != null); } 当我们使用方式一,而且第二个參数传入null的时候,默认调用的是以下的方法,而且attachToRoot是false public View inflate(int resource, ViewGroup root, boolean attachToRoot) { if (DEBUG) System.out.println("INFLATING from resource: " + resource); XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } } 在这一个方法中,pull解析器将资源id转化成XmlResourceParser对象,又传给了第四种方式,所以我们须要重点看的还是第四种方式是怎样实现的 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); } if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs, false); } else { // Temp is the root view that was found in the xml View temp; if (TAG_1995.equals(name)) { temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } } 代码比較长,我们重点关注以下的代码 if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } 这些代码的意思就是,当我们传进来的root參数不是空的时候,而且attachToRoot是false的时候,也就是上面的TwoActivity的实现方式的时候,会给temp设置一个LayoutParams參数。那么这个temp又是干嘛的呢? <pre name="code" class="java">// We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } 如今应该明确了吧。当我们传进来的root不是null,而且第三个參数是false的时候。这个temp就被增加到了root中。而且把root当作终于的返回值返回了。而当我们设置root为空的时候,没有设置LayoutParams參数的temp对象。作为返回值返回了。 因此,我们能够得出以下的结论: 1.若我们採用convertView = inflater.inflate(R.layout.item_list, null);方式填充视图。item布局中的根视图的layout_XX属性会被忽略掉。然后设置成默认的包裹内容方式 2.假设我们想保证item的视图中的參数不被改变,我们须要使用convertView = inflater.inflate(R.layout.item_list, parent,false);这样的方式进行视图的填充 3.除了使用这样的方式,我们还能够设置item布局的根视图为包裹内容,然后设置内部控件的高度等属性。这样就不会改动显示方式了。 最后,给出那篇文章的链接http://blog.jobbole.com/72156/大家能够去看看 本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5230670.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Java微服务开发指南 -- 集群管理、失败转移和负载均衡的实践

集群管理、失败转移和负载均衡的实践 在前一章节中,我们快速的介绍了集群管理、Linux容器,接下来让我们使用这些技术来解决微服务的伸缩性问题。作为参考,我们使用的微服务工程来自于第二、第三和第四章节(Spring Boot、Dropwizard和WildFly Swarm)中的内容,接下来的步骤都适合上述三款框架。 开始 我们需要将微服务打包成为Docker镜像,最终将其部署到Kubernetes,首先进入到项目工程hola-springboot,然后启动jboss-forge,然后安装fabric8插件,这个插件使我们安装maven插件变得非常容易。 $ cd ~/Documents/workspace/microservices-camp/ weipengktekiMBP:microservices-camp weip

优秀的个人博客,低调大师

【Android开发坑系列】之经常被忽略的背景图片内存泄露

我们平时设置图片的时候,几乎都忘记回收老的(背景)图片,比如: TextView.setBackgroundDrawable() TextView.setBackgroundResource() ImageView.setImageDrawable() ImageView.setImageResource() ImageView.setImageBitmap() 这样造成内存浪费,积少成多,整个软件可能浪费不少内存。 如果记得优化,整个软件的内存占用会有10%~20%的下降。 // 获得ImageView当前显示的图片 Bitmap bitmap1 = ((BitmapDrawable) imageView.getBackground()).getBitmap(); Bitmap bitmap2 = Bitmap.createBitmap(bitmap1, 0, 0, bitmap1.getWidth(),bitmap1.getHeight(), matrix, true); // 设置新的背景图片 imageView.setBackgroundDrawable(new BitmapDrawable(bitmap2)); // bitmap1确认即将不再使用,强制回收,这也是我们经常忽略的地方 if (!bitmap1.isRecycled()) { bitmap1.recycle(); } 看上面的代码,设置新的背景之后,老的背景确定不再使用,则应该回收。 封装如下(仅针对setBackgroundXXX做了封装,其他的原理类同): /** * 给view设置新背景,并回收旧的背景图片<br> * <font color=red>注意:需要确定以前的背景不被使用</font> * * @param v */ @SuppressWarnings("deprecation") public static void setAndRecycleBackground(View v, int resID) { // 获得ImageView当前显示的图片 Bitmap bitmap1 = null; if (v.getBackground() != null) { try { //若是可转成bitmap的背景,手动回收 bitmap1 = ((BitmapDrawable) v.getBackground()).getBitmap(); } catch (ClassCastException e) { //若无法转成bitmap,则解除引用,确保能被系统GC回收 v.getBackground().setCallback(null); } } // 根据原始位图和Matrix创建新的图片 v.setBackgroundResource(resID); // bitmap1确认即将不再使用,强制回收,这也是我们经常忽略的地方 if (bitmap1 != null && !bitmap1.isRecycled()) { bitmap1.recycle(); } } /** * 给view设置新背景,并回收旧的背景图片<br> * <font color=red>注意:需要确定以前的背景不被使用</font> * * @param v */ @SuppressWarnings("deprecation") public static void setAndRecycleBackground(View v, BitmapDrawable imageDrawable) { // 获得ImageView当前显示的图片 Bitmap bitmap1 = null; if (v.getBackground() != null) { try { //若是可转成bitmap的背景,手动回收 bitmap1 = ((BitmapDrawable) v.getBackground()).getBitmap(); } catch (ClassCastException e) { //若无法转成bitmap,则解除引用,确保能被系统GC回收 v.getBackground().setCallback(null); } } // 根据原始位图和Matrix创建新的图片 v.setBackgroundDrawable(imageDrawable); // bitmap1确认即将不再使用,强制回收,这也是我们经常忽略的地方 if (bitmap1 != null && !bitmap1.isRecycled()) { bitmap1.recycle(); } } 本文转自Kai的世界,道法自然博客园博客,原文链接:http://www.cnblogs.com/kaima/p/3497525.html,如需转载请自行联系原作者。

优秀的个人博客,低调大师

Android开发八 “尚未注册网络”错误信息的解决办法

打开Android模拟器时,出现无信号,拔打电话或发短信时,提示“尚未注册网络”错误信息的解决方案如下。 场景一:你的电脑没有连接上互联网,同时也没有在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:192.168.1.100 子网掩码:255.255.255.0 默认网关:192.168.1.100 首选DNS服务器:192.168.1.100 场景二:你的电脑没有连接上互联网,但在局域网。 解决办法:右键点击网上邻居,选择"属性",在网络连接窗口中右键点击"本地连接",选择"属性",设置TCP/IP属性如下: IP地址:设置成你所在局域网的IP,如:192.168.1.100 子网掩码:设置成你所在局域网的掩码,如:255.255.255.0 默认网关:设置成你所在局域网的网关,一般网关的IP格式为:*.*.*.1,如:192.168.1.1 首选DNS服务器:设置成你所在局域网的路由器IP,一般路由器的IP格式为:*.*.*.1,如:192.168.1.1 最后一种解决方案是:让你的电脑连接上互联网。 本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/archive/2012/04/03/2431259.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册