转自:http://www.shejidaren.com/ui-shi-jie-wu-cha.html
长宽 400px 的正方形与长宽 400px 的圆形哪一个更大?假如这样问你的话,那么正确答案当然是一样大。但是来看看下面这张图,长宽各 400px 的两个图形看起来并不一样大。你的眼睛告诉你 400px 的正方形比 400px 的圆形更大一些。物体的物理尺寸是一样的,但视觉尺寸却有可能不一样的。
为了更加准确地证明这个现象的存在,下面这张图给出辅助线,大家好好看看。
我们改变一下圆形的尺寸,看看现在这两个图形的视觉尺寸有没有更接近一些?
每个人的感官可能都不一样,但对于我来说,调整尺寸的后的两个图形看起来才是一样大的,至少也不会像图一一样,让人第一眼就认为正方形比较大。为啥会这样?因为我将圆的直径增加了 50px。
现在我们将图形都叠起来看,看看为什么会产生这种那么明显的误差。400px 的两个图形叠在一起,你会发现整个圆形都被包裹在了正方形之内,而正方形多出的四个面积巨大的 a 区域就是造成这种视觉误差的原因。再将 400px 的正方形与 450px 的圆形叠在一起,正方形无法将整个圆形包裹在内了,圆形超出的四个 b 区域又与 正方形多出来的 a 区域在视觉上互相抵消,所以 450px 的圆形与 400px 的正方形在视觉尺寸上更接近,也就是我们常说的“一样大”。
不仅是圆与方,所有的图形都能够造成这样的偏差。当我们追求“看起来一样大”这个目标的时候,某些形状的物理尺寸应该更大一些。
这个现象对于界面造成的设计会有哪一些呢?譬如说,当绘制一套 icon 的时候,我们当然是追求每个 icon 都看起来一样大对吧?但假如我们只通过物理尺寸来进行量度的画,画出来的 icon 必然会个大个小,乌七八糟,更伤的是,这种问题经常发生,手机里随便打开个 app 都能发现这样的问题。
(更多…)
// 参数说明: // parser 解析布局的解析器 // context 当前加载布局的上下文对象 // parent 父容器 // attrs 属性集合(XML该节点的属性集合) private void parseInclude(XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; // 判断 Include标签是否在 ViewGroup容器之内,因为 include 标签只能存在于 ViewGroup 容器之内。 if (parent instanceof ViewGroup) { //------------------<第一部分>-------------------// //当开发者设置 include 主题属性时,可以覆盖被 include 包裹View的主题属性。 //但是这种操作很少会使用。 //所以如果被包裹 View 设置主题属性,我们在设置就会出现覆盖效果。 //以 include 标签的主题属性为最终的主题属性 //提取出 include 的 thme 属性,如果设置了 them 属性,那么include 包裹的View 设置的 theme 将会无效 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); //------------------<第二部分>-------------------// //如果这个属性是指向主题中的某个属性,我们必须设法得到主题中layout 的资源标识符 //先获取 layout 属性(资源 id)是否设置 int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { //如果没直接设置布局的资源 id,那么就检索?attr/name这一类的 layout 属性 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() <= 0) { throw new InflateException("You must specify a layout in the" + " include tag: "); } //从 ?attr/name 这一类的属性中,获取布局属性 layout = context.getResources().getIdentifier(value.substring(1), null, null); } //这个布局资源也许存在主题属性中,所以需要去主题属性中解析 if (mTempValue == null) { mTempValue = new TypedValue(); } if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) { layout = mTempValue.resourceId; } //------------------<第三部分>-------------------// if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); throw new InflateException("You must specify a valid layout " + "reference. The layout ID " + value + " is not valid."); } else { final XmlResourceParser childParser = context.getResources().getLayout(layout); try { final AttributeSet childAttrs = Xml.asAttributeSet(childParser); while ((type = childParser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty. } if (type != XmlPullParser.START_TAG) { throw new InflateException(childParser.getPositionDescription() + ": No start tag found!"); } final String childName = childParser.getName(); if (TAG_MERGE.equals(childName)) { //解析 Meger 标签 rInflate(childParser, parent, context, childAttrs, false); } else { //根据 name名称来创建View final View view = createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; //获取 View 的 id 和其 Visiable 属性 final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.Include); final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID); final int visibility = a.getInt(R.styleable.Include_visibility, -1); a.recycle(); //需要将 Parent中的 LayoutParams 设置为其 Params 属性。 //如果 Parent 没有通用的 Params,那么就会抛出Runtime 异常 //然后会为其设置 include 包裹内容的通用 Params, ViewGroup.LayoutParams params = null; try { params = group.generateLayoutParams(attrs); } catch (RuntimeException e) { // Ignore, just fail over to child attrs. } if (params == null) { params = group.generateLayoutParams(childAttrs); } view.setLayoutParams(params); // 解析子标签 rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); } // 加载include内容时,需要直接设置其 可见性 switch (visibility) { case 0: view.setVisibility(View.VISIBLE); break; case 1: view.setVisibility(View.INVISIBLE); break; case 2: view.setVisibility(View.GONE); break; } //添加至父容器中 group.addView(view); } } finally { childParser.close(); } } } else { throw new InflateException(" can only be used inside of a ViewGroup"); } LayoutInflater.consumeChildElements(parser); }
项目开发计划顺利通过审核,进入实施阶段