[转载](转)TreeView使用集锦 - 阿东hd - 博客园

mikel阅读(1094)

[转载](转)TreeView使用集锦 – 阿东hd – 博客园.

ASP.NET TreeView安装、使用(如何将TreeView打包发布)(带CheckBox选择框的TreeView的初始化,TreeView客户端操作:选择父节点后自动选择所有子节点,子节点选择后自动选择父节点)(TreeView节点精确定位)
http://www.cnblogs.com/adandelion/archive/2005/11/29/235681.html

http://ghost527.cnblogs.com/archive/2005/12/30/308363.html

http://www.aspxboy.com/private/showthread.asp?postid=840

1.下载地址
http://msdn.microsoft.com/downloads/samples/internet/ASP_DOT_NET_ServerControls/WebControls/default.asp
下载后是后缀为bat的版本
(1)bulid.将bulid.bat的路径指向csc.exe所在路径,生成Microsoft.Web.UI.WebControls.dll。
(2)在wwwroot下创建空目录webctrl_client\1_0。
(3)将build\Runtime下的文件拷至webctrl_client\1_0下。
(4)选择工具箱的自定义工具箱,添加Microsoft.Web.UI.WebControls.dll。
有些麻烦
但如果你能找到后缀是msi的自动安装版本,直接下一步就行(我一直用这个版本,hoho)
安装后,通过“自定义工具箱”->“.net框架组件”把TreeView添加到工具箱里

2.运行时无法显示
一般是TreeView的版本问题,最好下载英文版自动安装版本重新安装,安装前应该先到添加删除程序里卸掉原版本

3.显示格式出错(非树状显示)
TreeView要求客户端浏览器版本为IE5.5及以上,最好要求客户端升级为IE6.0

4.框架里使用TreeView
设置NavigateUrl、Target属性,可更新另外的Frame

5.找不到TreeNode类
使用TreeView,最好添加namespace:using Microsoft.Web.UI.WebControls;

6.遍历TreeView节点(递归算法)
private void Page_Load(object sender, System.EventArgs e)
{
GetAllNodeText(TreeView1.Nodes);
}
void GetAllNodeText(TreeNodeCollection tnc)
{
foreach(TreeNode node in tnc)
{
if(node.Nodes.Count!=0)
GetAllNodeText(node.Nodes);
Response.Write(node.Text + ” “);
}
}

7.得到node结点的父节点
TreeNode pnode;
if(node.Parent is TreeNode)
pnode=(TreeNode)node.Parent;
else
//node is root node

8.修改TreeView样式(示例)
<iewc:TreeView id=”TreeView1″ runat=”server” HoverStyle=”color:blue;background:#00ffCC;” DefaultStyle=”background:red;color:yellow;” SelectedStyle=”color:red;background:#00ff00;”>
用代码:
TreeView1.DefaultStyle[“font-size”] = “20pt”;

9.展开时不提交,改变选择节点时才提交
将autopostback设置成false;
在body里添加  <body  onload=”initTree()”>
然后在PageLoad里写:
string  strTreeName  =  “TreeView1”;
string  strRef  =  Page.GetPostBackEventReference(TreeView1);
string  strScript  =  “<script  language=\”JavaScript\”>  \n”  +  “<!–  \n”  +  ”            function  initTree()  {  \n”  +”                        ”  +  strTreeName  +  “.onSelectedIndexChange  =  function()  {  \n”  +    “if  (event.oldTreeNodeIndex  !=
event.newTreeNodeIndex)  \n”  +  “this.queueEvent(‘onselectedindexchange’,  event.oldTreeNodeIndex  +  ‘,’  +  event.newTreeNodeIndex);  \n”  +    “window.setTimeout(‘”  +  strRef.Replace(“‘”,”\\’“)    +  “‘,  0,  ‘JavaScript’);  \n”  +    ”                        }  \n”  +      ”            }  \n”  +    “//  –>  \n”  +  “</script>”;
Page.RegisterClientScriptBlock(“InitTree”,strScript  );

这样就只有你点击的节点更改的时候才提交!

10.TreeView结合XML
把XML文件设置为如下格式,然后直接设置TreeNodeSrc为该XML文件就行
<?xml version=”1.0″ encoding=”GB2312″?>
<TREENODES>
<TREENODE TEXT=”node0″ EXPANDED=”true”>
<TREENODE TEXT=”node1″/>
<TREENODE TEXT=”node2″/>
</TREENODE>
<TREENODE TEXT=”node3″ NavigateURL=”3.aspx”/>
</TREENODES>
或者用代码
TreeView1.TreeNodeSrc=”a.xml”;
TreeView1.DataBind();

 

客户端控制TreeView
http://expert.csdn.net/Expert/topic/1382/1382892.xml

1.设置所选节点,如选中第二个节点
function SetSelNode()
{
TreeView1.selectedNodeIndex=”1″;
}

2.得到所选节点的Text,ID或NodeData
function GetAttribute()
{
alert(TreeView1.getTreeNode(TreeView1.selectedNodeIndex).getAttribute(“Text”));
}
替换Text为ID或NodeData,可分别得到所选节点的ID或NodeData

3.修改节点属性,如修改第一个节点的Text
function ModifyNode()
{
var node=TreeView1.getTreeNode(“0”);
node.setAttribute(“Text”,”hgknight”);
}

4.得到点击节点
function TreeView1.onclick()
{
alert(TreeView1.getTreeNode(TreeView1.clickedNodeIndex).getAttribute(“Text”));
}

5.添加节点
function AddNode()
{
var node=TreeView1.createTreeNode();
node.setAttribute(“Text”,”hgknight”);
TreeView1.add(node);
}

6.js遍历所有节点
 var AllRootNode=new Array();
AllRootNode=TreeView1.getChildren();
AlertNode(AllRootNode);

function AlertNode(NodeArray)
{
if(parseInt(NodeArray.length)==0)
return;
else
{
for(i=0;i<NodeArray.length;i++)
{
var cNode;
cNode=NodeArray[i];
alert(cNode.getAttribute(“Text”));
if(parseInt(cNode.getChildren().length)!=0)
AlertNode(cNode.getChildren());
}
}
}

[转载]ASP.NET MVC3不能正确识别JSON中的Enum枚举值 - dotnetgeek - 博客园

mikel阅读(1177)

[转载]MVC3不能正确识别JSON中的Enum枚举值 – dotnetgeek – 博客园.

一、背景

在MVC3项目里,如果Action的参数中有Enum枚举作为对象属性的话,使用POST方法提交过来的JSON数据中的枚举值却无法正确被识别对应的枚举值。

二、Demo演示

为了说明问题,我使用MVC3项目创建Controller,并且创建如下代码演示:

复制代码
    //交通方式枚举
    public enum TrafficEnum
    {
        Bus = 0,
        Boat = 1,
        Bike = 2,
    }
    public class Person
    {
        public int ID { get; set; }
        public TrafficEnum Traffic { get; set; }
    }

    public class DemoController : Controller
    {
        public ActionResult Index(Person p)
        {
            return View();
        }
    }
复制代码

网站生成成功之后,就可以使用Fiddler来发送HTTP POST请求了,注意需要的是,要在Request Headers加上请求头content-type:application/json,这样才能通知服务器端Request Body里的内容为JSON格式。

点击右上角的Execute执行HTTP请求,在程序断点情况下,查看参数p,属性ID已经正确的被识别到了值为9999,而枚举值属性Traffic却 被错认为枚举中的首个值Bus,这俨然是错误的,纵使你将Traffic修改成Bike,也就是值等于2,结果也是一样。

 

三、解决方法

方法一:

升级MVC4,亲测在MVC4项目下,这个问题已经被修复了;

方法二:

假若因为各种原因,项目不想或者不能升级为MVC4,可以在MVC3项目上做些改动,亦可修复这个问题,

1、在项目中,新建一个类,加入以下代码,需要引用一下 using System.ComponentModel;  using System.Web.Mvc; 命名空间;

复制代码
    /// <summary>
    /// 处理在MVC3下,提交的JSON枚举值在Controller不能识别的问题
    /// </summary>
    public class EnumConverterModelBinder : DefaultModelBinder
    {
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            var propertyType = propertyDescriptor.PropertyType;
            if (propertyType.IsEnum)
            {
                var providerValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                if (null != providerValue)
                {
                    var value = providerValue.RawValue;
                    if (null != value)
                    {
                        var valueType = value.GetType();
                        if (!valueType.IsEnum)
                        {
                            return Enum.ToObject(propertyType, value);
                        }
                    }
                }
            }
            return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
        }
    }
复制代码

2、在Global.asax的Application_Start方法中,进行EnumConverterModelBinder类的实例化操作:

       protected void Application_Start()
        {
            //处理在MVC3下,提交的JSON枚举值在Controller不能识别的问题
            ModelBinders.Binders.DefaultBinder = new EnumConverterModelBinder();
        }

进行配置改造之后,我再次生成网站,重新发送HTTP请求看,MVC Action中的参数里的枚举就能被正确的识别到了。

 

四、研究

我觉得这应该是mvc3里面一个小小的缺陷吧,随着mvc的升级,这已经在新版本里被完善修复了,可还用着mvc3的人如果在项目中遇到这个问题,可以研究一下。

遇到一个问题,去百度谷歌找解决方案是可以,但是复制粘贴完代码之后,最好问下自己,为什么这样可以解决问题。

从现象和解决代码中猜想,应该是在MVC生命周期中的Model Binders 这一环节出了问题。

     因为MVC已经开源了,所以我尝试着调试源码,首先下载MVC3的源码,其他项目可以移除,只保留红色框中的项目即可,然后新建一个MVC3测试项目,并且将此测试项目的system.web.mvc引用移除,转而引用本解决方案中的system.web.mvc 项目,这样子,我们才可以对MVC源码进行调试操作。

搜回来的代码中可知,我们自定义的类继承DefaultModelBinder父类,并且重写了GetPropertyValue方法,那我们就从这点开始,在MVC3源码中的System.Web.MVC项目中找到该类,在此方法上插入断点。

F5调试程序,发送一个POST请求。

其实BindProperty方法是会被多次执行的,BindProperties方法会对请求的实体类的属性进行遍历,每一个属性都要经过BindProperty方法的处理;

现在已经截获到第一个属性ID了。

紧接着,程序进入propertyBinder.BindModel 方法。

只贴部分关键代码了,通过bindingContextValueProvider 获得属性的相关信息,如果不等于null的话,转到执行BindSimpleModel 方法。

BindSimpleModel方法里,首先通过Type.IsInstanceOfType方法判断确定指定的对象是否是当前 Type 的实例,如果是,则直接返回rawValue,这里的属性类型是Int32类型,返回True符合条件,所以直接把rawValue给返回去了。

第一个Int32类型属性的部分关键代码执行到这里就已经确认到值了,接下来,我们看出了问题的Enum枚举类型属性。

 循环来到了第二个属性了,这时我留意到有个Model属性,对比Int32类型执行的时候,这个属性当时为0,而此时则为Bus,可见这是一个默认值,指定枚举中值为0的那个类型(即使你不为枚举显式指定值),同样的,经过BindModel方法来到了BindSimpleModel方法。

此时,对比Int32类型的属性ID,这次ModelType.IsInstanceOfType(valueProvideResult.RawValue)False,并且接下来不是string类型就执行以下的判断,也不是数组类型,所以,来到了最后一个,根据绿色的注释可以看出,这应该是一个判断是否collection集合类型的方法,Enum都不是,所以,返回了Null

这时,Type collectionType变量为Null,执行最后一个case 3

ConvertProviderResult方法里,也进行了一系列的类型判断转换,目的就是将JSON中的数字类型转换成枚举值,但是执行过程中抛出异常了,原因是

No type converter can convert between these types ” 也就是说,在MVC3的机制中,并没有相应的type converter来处理数值与枚举的对应。

经过以上这些处理方法,都没完成把对应的值确认下来,怎么给原来的BindProperty 老大方法交差呢,所以,小的只好将Value=Null 和 modelState.Errors 模型错误状态信息如实带回去了,让老大决定怎么做,老大后面处理这里有点绕,但是我看源码估计也是拿默认值来充当Value了,所以就造成了JSON传过来的值与对应枚举的值不对应的情况,无论传什么值,结果都是第一个枚举的值。

 五、总结

这篇文章只是我在工作上遇到的一个小问题,然后有点小兴趣就从源码的角度上来研究和分析,缺乏理论的依据,因为之前没有很深入的去研究MVC的底层运行机制与生命周期,所以这方面还需要得加强学习一下,如果你也有兴趣,可以下载我修改好的源码来分析一下,甚至可以下载MVC4的源码来进行对比。

[转载]c# asp.net微信公众平台开发(3)微信消息封装及反射赋值 - 沫尘 - 博客园

mikel阅读(1047)

[转载]asp.net微信公众平台开发(3)微信消息封装及反射赋值 – 沫尘 – 博客园.

上一篇已经搭建好整体框架,实现了入口的验证,   验证通过后就交给LookMsgType方法处理,LookMsgType方法主要是对微信发来的不同的消息进行分解,不同的类型交给业务逻辑层不同的方 法处理,   对不同类型的消息判断,可以用if,也可以用switch   一般来说超过5个的if用switch会更好, 这里贴出LookMsgType方法:

public void LookMsgType(string msgType)
{

#region 判断消息类型
switch (msgType)
{
case "text":
RText mText = new RText();
mText = ReadXml.GetModel(mText, xmlModel);
BLLWei.DoText(dbHome, mText);//文本消息
break;
case "image":
RImg mImg = new RImg();
mImg = ReadXml.GetModel(mImg, xmlModel);
BLLWei.DoImg(dbHome,mImg);//图片
break;
case "voice": //声音
RVoice mVoice = new RVoice();
mVoice = ReadXml.GetModel(mVoice, xmlModel);
BLLWei.DoVoice(dbHome,mVoice);
break;

case "video"://视频
RVideo mVideo = new RVideo();
mVideo = ReadXml.GetModel(mVideo, xmlModel);
BLLWei.DoVideo(dbHome, mVideo);
break;

case "location"://地理位置
RLocation mLocation = new RLocation();
mLocation = ReadXml.GetModel(mLocation, xmlModel);
BLLWei.DoLocation(dbHome,mLocation);
break;
case "link"://链接
RLink mLink = new RLink();
mLink = ReadXml.GetModel(mLink, xmlModel);
BLLWei.DoLink(dbHome,mLink);
break;
#region 事件
case "event":

switch (ReadXml.ReadModel("Event", xmlModel))
{
case "subscribe":

if (ReadXml.ReadModel("EventKey", xmlModel).IndexOf("qrscene_") &gt;= 0)
{
RCodeNotSub mNotSub = new RCodeNotSub();
mNotSub = ReadXml.GetModel(mNotSub, xmlModel);
BLLWei.DoCodeNotSub(dbHome,mNotSub);//未关注的新用户,扫描带参数的二维码关注
}
else
{
RSub mSub = new RSub();
mSub = ReadXml.GetModel(mSub, xmlModel);
BLLWei.DoSub(dbHome,mSub);//普通关注
}
break;
case "unsubscribe":
RUnsub mUnSub = new RUnsub ();
mUnSub = ReadXml.GetModel(mUnSub, xmlModel);
BLLWei.DoUnSub(dbHome,mUnSub);//取消关注
break;

case "SCAN":
RCodeSub mCodeSub = new RCodeSub();
mCodeSub = ReadXml.GetModel(mCodeSub, xmlModel);
BLLWei.DoCodeSub(dbHome,mCodeSub);//已经关注的用户扫描带参数的二维码
break;
case "LOCATION"://用户上报地理位置

RSubLocation mSubLoc = new RSubLocation();
mSubLoc = ReadXml.GetModel(mSubLoc, xmlModel);

BLLWei.DoSubLocation(dbHome, mSubLoc);
break;
case "CLICK"://自定义菜单点击

RMenuClick mMenuClk = new RMenuClick();
mMenuClk = ReadXml.GetModel(mMenuClk, xmlModel);
BLLWei.DoMenuClick(dbHome, mMenuClk);
break;
case "VIEW"://自定义菜单跳转事件

RMenuView mMenuVw = new RMenuView();
mMenuVw = ReadXml.GetModel(mMenuVw, xmlModel);
BLLWei.DoMenuView(dbHome, mMenuVw);
break;
};
break;
#endregion
}
#endregion
}

外层switch判断msgtype,   在event类型时,再次switch判断具体的事件类型(关注、取消关注、自定义菜单事件等),  至此所有的微信发来的消息都有处理了,在上面代码中用到消息模型以及ReadXml.GetModel方法给模型赋值, 赋值之后传递给业务逻辑层对应的方法处理,  下面写出消息封装和给模型赋值的方法。

1、消息封装:

对所有微信发来的消息进行封装, 在datamodel中建一个Receive文件夹和一个send文件夹,在其中分别建立对应消息的类,完成之后,完整的datamodel类库如下图:

 

举例

—–接收消息:

文本消息RText.cs

public class RText
    {
        public string ToUserName { get; set; }// 开发者微信号
        public string FromUserName { get; set; }// 用户号(OpenID)
        public long CreateTime { get; set; }// 创建时间
        public string MsgType { get; set; } //消息类型
        public string Content { get; set; }//内容
        public long MsgId { get; set; }//消息ID

    }

自定义菜单点击RMenuClick.cs

public class RMenuClick
    {
        public string ToUserName { get; set; }// 开发者微信号
        public string FromUserName { get; set; }// 用户号(OpenID)
        public long CreateTime { get; set; }// 创建时间
        public string MsgType { get; set; } //消息类型

        public string Event { get; set; }//事件类型
        public string EventKey { get; set; }//事件key
        
    }

其他也都类似,不一一列举。

—–发送消息

发送文本消息SText.cs

public class SText
    {



        public string ToUserName { get; set; }// 用户号(OpenID)
        public string FromUserName { get; set; }// 开发者微信号

        public long CreateTime { get; set; }// 创建时间

        public string MsgType { get { return "text"; } } //消息类型

        public string Content { get; set; }//内容


    }

SText
[/sharp]
发送图文消息SNews.cs

namespace DataModel.Send
{
    public class SNews
    {
        public string ToUserName { get; set; }// 用户号(OpenID)
        public string FromUserName { get; set; }// 开发者微信号

        public long CreateTime { get; set; }// 创建时间

        public string MsgType { get { return "news"; } } //消息类型

        public int ArticleCount { get; set; }//图文个数

        public List<ArticlesModel> Articles { get; set; }//图文列表
    }
    public class ArticlesModel //默认第一条大图显示
    {
        public string Title { get; set; }//标题
        public string Description { get; set; }//描述
        public string PicUrl { get; set; }//图片链接  
        public string Url { get; set; }//点击之后跳转的链接

    }
}

发送图文消息

在发送图文消息中,因为回复给微信的图文消息中,具体的图文内容是多条(最多可以10条),所以单独会有ArticlesModel。 后面文章会写出图文消息的发送。

2、通过反射给model赋值

在上篇文章写的入口处,已经有了解析xml的方法,现在封装了消息,通常的做法,是每次用到对应的model就手动写代码赋值, 而我这里LookMsgType方法中所有给消息赋值时全用的ReadXml.GetModel这同一个方法, 这里用的就是反射,方法如下:

/// <summary>
        /// 通过反射给接收消息model赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        public static T GetModel<T>(T model, Dictionary<string, string> xmlModel) where T : class
        {
            var m = model.GetType();
            foreach (PropertyInfo p in m.GetProperties())
            {
                string name = p.Name;
                if (xmlModel.Keys.Contains(name))
                {
                    string value=xmlModel.Where(x => x.Key == name).FirstOrDefault().Value;
                    p.SetValue(model,
                    string.IsNullOrEmpty(value) ? null : Convert.ChangeType(value, p.PropertyType), null); 
                }
            }
            return model;
        }

T model 就是要使用的消息类, xmlmodel是在入口处传递进来的解析的微信发来的xml信息, 这样,就不需要每次手动写代码赋值了。

好了,此篇实现了lookmsgtype方法, 实现了消息封装和反射赋值, 接下去就是到了业务逻辑层中的处理和具体实现了…

[转载]编写高质量代码改善C#程序的157个建议[勿选List做基类、迭代器是只读的、慎用集合可写属性] - aehyok - 博客园

mikel阅读(1106)

[转载]编写高质量代码改善C#程序的157个建议[勿选List做基类、迭代器是只读的、慎用集合可写属性] – aehyok – 博客园.

前言

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

建议23、避免将List<T>作为自定义集合类的基类

建议24、迭代器应该是只读的

建议25、谨慎集合属性的可写操作

建议23、避免将List<T>作为自定义集合类的基类

 如果要实现一个自定义的集合类,最好不要以List<T>作为基类,而应该扩展相应的泛型接口,通常是 Ienumerable<T>和ICollection<T>(或ICollection<T>的子接口,如 IList<T>。

public class Employee1:List<Employee>
public class Employee2:IEnumerable<Employee>,ICollection<Employee>

不过,遗憾的是继承List<T>并没有带来任何继承上的优势,反而丧失了面向接口编程带来的灵活性,而且可能不稍加注意,隐含的Bug就会接踵而至。

来看一下Employee1为例,如果要在Add方法中加入一点变化

    public class Employee
    {
        public string Name { get; set; }
    }
public class Employee1:List
{
public new void Add(Employee item)
{
item.Name += "Changed";
base.Add(item);
}
}

进行调用

public static void Main(string[] args)
{
Employee1 employee1 = new Employee1() {
new Employee(){Name="aehyok"},
new Employee(){Name="Kris"},
new Employee(){Name="Leo"}
};
IList employees = employee1;
employees.Add(new Employee(){Name="Niki"});
foreach (var item in employee1)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}

结果竟然是这样

这样的错误如何避免呢,所以现在我们来来看看Employee2的实现方式

public class Employee2:IEnumerable,ICollection
{
List items = new List();
public IEnumerator GetEnumerator()
{
return items.GetEnumerator();
}

///省略
}

这样进行调用就是没问题的

public static void Main(string[] args)
{
Employee2 employee1 = new Employee2() {
new Employee(){Name="aehyok"},
new Employee(){Name="Kris"},
new Employee(){Name="Leo"}
};
ICollection employees = employee1;
employees.Add(new Employee() { Name = "Niki" });
foreach (var item in employee1)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}

运行结果

建议24、迭代器应该是只读的

 前端时间在实现迭代器的时候我就发现了这样一个问题,迭代器中只有GetEnumeratior方法,没有SetEnumerator方法。所有的集合也没有一个可写的迭代器属性。原来这里面室友原因的:

其一:这违背了设计模式中的开闭原则。被设置到集合中的迭代可能会直接导致集合的行为发生异常或变动。一旦确实需要新的迭代需求,完全可以创建一个新的迭代器来满足需求,而不是为集合设置该迭代器,因为这样做会直接导致使用到该集合对象的其他迭代场景发生不可知的行为。

其二:现在,我们有了LINQ。使用LINQ可以不用创建任何新的类型就能满足任何的迭代需求。

关于如何实现迭代器可以来阅读我这篇博文http://www.cnblogs.com/aehyok/p/3642103.html

现在假设存在一个公共集合对象,有两个业务类需要对这个集合对象进行操作。其中业务类A只负责将元素迭代出来进行显示:

IMyEnumerable list = new MyList();
IMyEnumerator enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{
object current = enumerator.Current;
Console.WriteLine(current.ToString());
}
Console.ReadLine();

业务类B出于自己的某种需求,需要实现一个新的针对集合对象的迭代器,于是它这样操作:

MyEnumerator2 enumerator2 = new MyEnumerator2(list as MyList);
(list as MyList).SetEnumerator(enumerator2);
while (enumerator2.MoveNex())
{
object current = enumerator2.Current;
Console.WriteLine(current.ToString());
}
Console.ReadLine();

问题的关键就是,现在我们再回到业务类A中执行一次迭代显示,结果将会是B所设置的迭代器完成输出。这相当于BG在没有通知A的情况下对A的行为进行了干扰,这种情况应该避免的。

所以,不要为迭代器设置可写属性。

建议25、谨慎集合属性的可写操作

 如果类型的属性中有集合属性,那么应该保证属性对象是由类型本身产生的。如果将属性设置为可写,则会增加抛出异常的几率。一般情况下,如果集合属性没有值,则它返回的Count等于0,而不是集合属性的值为null。我们来看一段简单的代码:

public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
public class StudentTeamA
{
public List Students { get; set; }
}
class Program
{
static List list = new List()
{
new Student(){Name="aehyok",Age=25},
new Student(){Name="Kris",Age=23}
};
static void Main(string[] args)
{
StudentTeamA teamA = new StudentTeamA();
Thread t1 = new Thread(() =&gt;
{
teamA.Students = list;
Thread.Sleep(3000);
Console.WriteLine("t1"+list.Count);
});
t1.Start();
Thread t2 = new Thread(() =&gt;
{
list= null;
});
t2.Start();
Console.ReadLine();
}
}

首先运行后报错了

这段代码的问题就是:线程t1模拟将对类型StudentTeamA的Students属性进行赋值,它是一个可读/可写的属性。由于集合属性是一个引用类型,而当前针对该属性对象的引用却有两个,即集合本身和调用者的类型变量list。

线程t2也许是另一个程序猿写的,但他看到的只有list,结果,针对list的修改会直接影响到另一个工作线程中的对象。在例子中,我们将 list赋值为null,模拟在StudentTeamA(或者说工作线程t1)不知情的情况下使得集合属性变为null。接着,线程t1模拟针对 Students属性进行若干操作,导致异常的抛出。

下面我们对上面的代码做一个简单的修改,首先,将类型的集合属性设置为只读,其次,集合对象由类型自身创建,这保证了集合属性永远只有一个引用:

public class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
public class StudentTeamA
{
public List Students { get;private set; }
public StudentTeamA()
{
Students = new List();
}
public StudentTeamA(IEnumerable list):this()
{
Students.AddRange(list);

}
}
class Program
{
static List list = new List()
{
new Student(){Name="aehyok",Age=25},
new Student(){Name="Kris",Age=23}
};
static void Main(string[] args)
{
StudentTeamA teamA = new StudentTeamA();
teamA.Students.AddRange(list);
teamA.Students.Add(new Student() { Name="Leo", Age=22 });
Console.WriteLine(teamA.Students.Count);
///另外一种实现方式
StudentTeamA teamB = new StudentTeamA(list);
Console.WriteLine(teamB.Students.Count);
Console.ReadLine();
}
}

修改之后,在StudentTemaA中尝试对属性Students进行赋值,就会发现如下问题

上面也发现了两种对集合进行初始化的方式。

[转载]移动app创业的7个认识误区 - 创业ABC - 创业邦

mikel阅读(1094)

[转载]移动app创业的7个认识误区 – 创业ABC – 创业邦.

互联网创业公司相比,移动互联网的生态系统其实还是很新的,因此,许多首次进入app开发领域的创业者对整个过程有一些错误的认识。

以下是进入这个领域里认识上的一些误区。避开它们,将帮助首次进入app开发市场的创业者们踏上正确的道路,并且让他们知道创建并且推广产品的过程中,会面临什么样的困难。

1.技术压倒一切:如果你并不具备技术背景,不考虑把技术作为创业公司的顶点,那么技术只是一个推动者——你的项目、你的公司和商业目的才是核心。你不必知道开发一个技术产品,代码该怎么写。雇用精通这门业务的人,或者外包给其他机构,你可以通过这些技术手段帮助你实现商业目标。

2.开发出产品,任务就完成了:如果你认为,你的app在app sotre上架了,你的工作就完成了,那你就大错特错了。开发一个app是创建公司的过程中最容易的事情,让用户一直使用你的app才是最困难的部分。

3.创意就是一切:如 果你的想法是独一无二的,以至于没有人曾经想到过,那么很有可能没有人会认同它。康奈尔大学的一篇公开发表的论文就曾得出这样的结论:创意越是新颖,这些 问题的不确定性就越高——这个想法是否可行、是否有用、是否无差错,是否可以被可靠地复制。这些都是关于执行的问题——你如何实现你的想法才是最重要的。 有很多伟大的想法没有成功,不是因为创意,是因为它们没有被很好地执行。

4.仅仅有个创意原型就可以获得融资:现在,科技创业企业的整体成本比起以前创建传统的软件公司来,已经大幅下降了不少。Hackathons大赛上,一个app几天之内就可以做出demo和第1个版本来。投资者们在考虑投资时,往往会寻找己经经过用户测试的,用户注册数量和用户留存率己经出现增长的产品。

5.可以向朋友对你的创意项目进行验证:询问某人对一个假想的产品反馈,你得到的也是一种想象中的回应。省点力气吧,不用去咨询那些“不必对意见承担后果”的人的意见,你应该去相信的,是那些真正使用你的app的人的意见。更好的一种做法是,从有偿使用你的app的人那里寻求建议。

6.营销意味着支出:当 然,付费营销是最简单的路径,但不一定是最有效的。销售增长的基础是,你的app确实是一个很好的产品,并且能让你的第一批客户感到满意。每一个成功的 app公司都是基于口碑才渐渐受到普遍欢迎的。人们倾向于支持脱颖而出的产品,付费营销可能会让你获得下载数量的增长,但不会留住用户。

7.获得资金是公司的首要任务:筹 集风险资金是你要做的最艰难的事情之一。当你想建立一个创业企业时,请专注于开发产品以及获得客户。假设在启动前,你不能保证能够获得足够的投资,那么你 的首要计划就始终应该是打入市场。你创业的雄心不应依赖于风险投资,但是你却一定要下决心成为一个成功的企业家,开拓自己的市场和用户。

作者: Rahul Varshneya,Arkenea LLC的创始人之一,该公司致力于帮助创业者开拓市场,并将他们的移动app商业化。

(Via:entrepreneur.创业邦编译。Entrepreneur内容授权创业邦独家合作,未经允许请勿转载)

[转载]改变手机IMEI的方法-月光博客

mikel阅读(1168)

[转载]改变手机IMEI的方法-月光博客.

某些移动应用会通过技术手段阻止用户使用,例如被陌陌封号了以后,使用者会注意到,自己的手机再也无法使用陌陌了,即便是再注册一个帐号也无济于事。本文给出一个办法:即通过PDroid修改app所能见到的IMEI,来合法安全地解决这个问题。

产生这种状况的原因:

1,通过在Android Package的Androidmanifest.xml文件中添加Android.permission.READ_PHONE_STATE对 象,android应用程序可以通过Android提供的API获取访问蜂窝设备的IMEI号码的权限。

2,在Android手机上,陌陌会索取手机的IMEI号码,并且把它当作用户校验的手段。当你试图在别的手机上登陆你的陌陌账户时,会被拒绝。同样,陌陌的运营者可以决定阻止带有特定IMEI的设备登陆。

3,每个手机都有一个独一无二的IMEI号码,这意味着IMEI和用户高度相关,和手机号码一样是一个可以精确追踪到用户的标记,只要不换手机,无论你使用什么帐号,IMEI都是不变的。

——有一个例外,山寨机的IMEI号码往往会不符合以上情况

问题出在哪里:

显然,陌陌是无权封禁设备的。因为它不可以具备追踪到设备的能力。换个说法,只要用户希望,用户就应当能获得阻断陌陌追踪到用户设备的能力。

IMEI与用户的高度相关决定了它是用户的隐私,只有用户的蜂窝网络运营商以及一些直接的利益相关者(如负责公共安全的政府部门,厂商在维修设备时)才可以无条件知晓特定IMEI与用户之间的联系。除此以外,用户理应可以自行决定哪些应用程序可以知晓自己设备的串号。

而陌陌将IMEI作为了用户登陆的必要条件。如果陌陌无法从Android.permission.READ_PHONE_STATE获得IMEI,它就不会登陆。

所以?

就像越狱苹果手机那样,我们需要一个由用户可行的,剥夺这种(可能是非法的)追踪到具体设备能力的手段。在罕见的情况下,使用二手设备的用户不得不通过这样的方法来绕过自己手机的前一个使用者被封禁,导致自己也不能用陌陌的故障。

简单地说:

既然形式上陌陌需要一个IMEI,那么就如其所愿,公平地给它一个李鬼IMEI吧。如果陌陌一旦获得了IMEI,只要内容不为空,它就会允许注册/登陆

以下介绍的内容无法改变手机本身的IMEI(这个东西是固化在更底层的地方,不是通过修改系统就能做到的),所以没有法律或安全上的问题;相反的,它会 给android用于向app提供IMEI的API做点手脚,让你可以自行决定,是否向应用程序提供IMEI,或者提供一个自定义/随机的IMEI。

实践以前,请检查一下是不是满足以下条件:

最重要的:因为接下来介绍的方法是公开的,涉及到的代码也是开源(PDroid patch的代码本体以GPLv3授权)的,因此以下的方法以“原样”提供,没有任何保证,实践者需要自行承担操作带来的一切后果(比如,包含且不限于设备损坏,被封号等状况)

1,设备需要满足的基本条件:

设备的bootloader必须解开(对于HTC设备,需要处于s-off状态)

仅可在这些系统实行Android 2.3(Gingerbread)/4.0(Ice Cream Sandwitch)/4.1(Jelly Bean)

Android必须允许root权限(意味着你可以通过superuser/superSU/LBE授权管理等为需要的app提供root权限),以使得PDroid patch可以工作,实际上PDroid app本身并不需要root权限。

Android必须deodexed

2,开发环境

对于Android 2.3,你需要:

一个安装了JDK1.6/1.7的windows环境,
.Net Framework 2.0虚拟机,
设备的recovery环境需要clockwork recovery(TWRP recovery可能不被支持)

对于Android 4.0,你需要:

一个安装了JDK1.6/1.7的linux、MAC或cygwin环境

或者:
在有足够java,android开发能力的前提下,你可以按照XDA-developers论坛上给出的源代码,指示,自行编译。

3,你需要掌握的知识

会给android手机刷机,某些情况下需要知道怎么解锁bootloader,以及通过fastboot
安装linux内核;可能需要知道最基本的linux终端(terminal)的使用方法

4,其它

如果是android 2.3,设备的framework.jar必须没有改动过
如果是android 4.0/4.1,只有特定的rom种类被支持。支持名单包括(到2012年8月25日为止):CM7,CM9,CM10,AOSP(Jelly Bean以及部分低级版本),AOKP,ParanoidAndroid,以及其它某些未指明的版本。

——CM是Cyanogen Mod的缩写,AOSP是Android Open Source Project的缩写,AOKP是Android Open Kang Project的缩写。

实际操作:

将会做什么?

通过给android系统打一个叫PDroid patcher的权限管理补丁,配合上专用的app(也就是PDroid app)后,可以给陌陌的app提供一个自定义IMEI号码,从而绕过它的IMEI检测系统。

具体步骤?

对于Android 2.3

1,下载PDroid作者svyat编译好的自动补丁制作工具PDroid Patcher

2,在recovery环境中将当前的ROM进行nandroid备份并导出。当然,也可以用全新的刷机包(aroma installer格式的可能不被支持)

3,启动PDroid Patcher,选择上一步准备好的备份/刷机包(.zip文件),点击patch后,软件会针对当前的.zip制作一对补丁,一个是用于打上PDroid补丁的,另一个是恢复包,用于打完补丁后的还原(在出故障,或不希望继续使用等场合下)

4,把上一步制作好的两个补丁复制到设备的SD卡(对于不支持SD卡的设备,则放在/sdcard分区),如果是安装全新系统,不要忘了刷机包。

5,进入recovery环境,安装PDroid补丁。新安装系统的,则先安装系统后再安装PDroid补丁。

6,到Google Play Store安装PDroid app;如手机没有Gapps,或因众所周知的原因而无法访问Play Store时,则可以到作者在XDA-developer的页面获取。

7,打开PDroid app,给陌陌指定一个IMEI号码即可——同样的,这个IMEI号码会和你新注册的陌陌帐号绑定。

对于Android 4.0/4.1

原作者svyat提供的补丁工具只适用于Android 2.3,因此我们需要mateorod的帖子中,适用于4.0/4.1的工具。这个工具只能在Linux、Mac,以及Cygwin环境下运行。所以实践 者可能需要一台安装了JDK(OpenJDK和Oracle开发的JDK都可以)的linux电脑/虚拟机,或者按照作者提供的方法自行配置cygwin 环境。

1,下载专用的补丁制作工具,解压至实践者自行指定的工作目录

2,在terminal中进入前面所说的工作目 录,以”./autopatcher ROM.zip pdroid”的形式运行,稍等一会,补丁(以及恢复包)就会制作完成。也可以” ./autopatcher ROM.zip pdroid,v6supercharger”的形式运行,这样补丁就会同时包括pdroid与supercharger V6(一个调整了默认Android系统内存管理的脚本)

接下来的步骤和前面相同。

iOS系统怎么办:

在iOS系统下(需要越狱),也有类似的应对方法。有一个叫UDIDFaker的工具可以为指定的app更改UDID,也可以用来绕过陌陌(可能是非法) 的限制。

类似的方法也可以用于whatsapp等强制要求IMEI才可工作的app,如果你不希望它们知晓你的IMEI的话。

可能需要的相关XDA-developers内容的地址以及参考:

mateorod  —  [Script][8-08][JB] Auto-Patcher
http://forum.xda-developers.com/showthread.php?t=1719408

svyat  –  [APP] PDroid – The Better Privacy Protection [2012-03-18: Bugfixes in Patch 1.32]
http://forum.xda-developers.com/showthread.php?t=1357056

[App] PDroidAddon Camera and Microphone Permission [Update: v1.2, 2012-08-08]
http://forum.xda-developers.com/showthread.php?t=1794411

IMEI
http://baike.baidu.com/view/90099.htm

来源:willproven投稿

[转载]加密与解密 ,URL请求保护,注册码-Android开发经验分享-eoe 移动开发者论坛 - Powered by Discuz!

mikel阅读(1200)

[转载]加密与解密 ,URL请求保护,注册码-Android开发经验分享-eoe 移动开发者论坛 – Powered by Discuz!.

懒得排版,懒得给代码,懒得写太多。大牛绕道。伸手党绕道。
加密分很多种,但是按其方向来分就两个。
单向加密,加密之后永远得不到最初的密码。
可加可解,加密之后通过双方约定的钥匙来解密。

用途:单向加密自然就不希望别人知道密码,在校验的时候只需要比较加密后的信息是否相同进行通过就可以了,可以保护原始信息,也是较为安全的方式。
MD5 SHA等等基本都是这个的思路。很多人会提到,这些东西安全个什么,稍微厉害的服务器就可以反找密码。试想,如果在MD5上在加上你自己的加密算法可能会有人解出来?
跑题…

可加可解:可加可解,既然要解,所以肯定加密后的密码不仅仅是用于校验,还有一部分信息是需要提取的,用于其他用途,至于干嘛,稍后例子详解。

URL请求保护

有人问,怎么保证数据库的信息安全,我需要怎么写,怎么加密?
其实我遇到这种问题很反感,太宽泛。但是本人又不是大牛,大哲,只好把一些琢磨的思路扔出来。
想把所有数据都加密再解密,除非是非常严格需求,否则根本不现实。尤其在Android这种移动设备上,仅仅是为了数据安全做这些东西。所以我是不建议在输入输出流上做文章。
因此就有了请求保护的想法,其实请求保护说穿了就是oath认证的一些东西。但我在这里不拔oath的东西,有兴趣的可以去研究。
下面就说一下实现思路,
简单的思路,定义一个软件私有key 做单向加密 , 服务器做同样处理看两次加密后结果是否一致,一致则通过。
这个方案有什么问题呢?你的加密后的口令一旦被泄露,就可以被所有人都使用了。你只能通过升级版本来改变key值。
稍微复杂的思路,定义一个软件私有key,做 单向加密Code1。时间戳做双向加密Code2,Code1和Code2做双向加密CodeSum。服务器 得到密码CodeSum 解密得到Code1和Code2。可以得到时间和校验口令。这里要注意,时间戳要自己定义时间延时量。一般根据网速定一个合理的范围,只要时间差的不多就 放行。
这个方案比较第一个已经相当不错了,黑客就算截取了你的一次请求,顶多就在几秒内可用。而且黑客加密复杂度极高做不到逆向解码。
扩展思路,上面的需求基本都是一个用户的信息,在实际处理中往往涉及到多用户的请求。只需要稍微对上面的加密方式进行修改就可以了,在加密因子里面加上加密的sessionID作为处理。

没有什么真知灼见,但是这样处理你是解决的每次请求,有效的避免了非法请求。也是互联网应用的不同,需要实效性。

注册码

注册码的校验分为很多情况,可以服务器校验,可以手机客户端校验。针对不同场景不同方案。
服务器校验每次激活需要联网HTTPS,然后客户端获取权限进行使用。流程:手机传递IMEI和激活码,验证成功后 服务器保存激活码,返回可以激活口令,客户端进行激活。扩展:给用户提供解绑定功能,和多台IMEI激活等设定。IMEI其实和PC的硬盘码 机器码十分类似。(注意:平板没IMEI)
客户端校验:用户不需要联网,在指定网站上根据IMEI购买后直接注册,一个IMEI对应一个手机。(稍微霸道)

写在后面:其实这些校验的东西说的再细,都要建立在优秀的混淆包,防DECODE的基础上,否则再强大的加密模式都毫无意义。但是一个好的加密方式会给你很多补救,也不会泄漏用户信息。(吐槽CSDN?)
明文要不得啊,只加SHA无异于明文啊!

[转载]常用上网增强类Chrome扩展-月光博客

mikel阅读(926)

[转载]常用上网增强类Chrome扩展-月光博客.

Chrome是个非常好用的浏览器,拥有丰富的扩展资源库,能够满足网民各种各样的需求,对于网民来说,通过Chrome扩展来增强上网体验是 一个基本需求,但是安装过多的扩展有容易耗费大量系统资源,今天月光博客就给大量挑选一些常用的上网增强类Chrome扩展,供大家参考。

LastPass:用于管理大量网站的密码,给不同网站设置不同的密码,支持自动登录,支持手机两步验证。建议在普通和隐身模式下都启用这个扩展。

Adblock Plus:广告屏蔽扩展,可以屏蔽不少网站的广告,包括屏蔽Youtube视频广告、Facebook广告、横幅广告以及其他广告,还可以自定义规则,在网络广告泛滥的年代里,可以做到眼不见心不烦。该扩展是一个社区驱动的开源项目 ,有不少志愿者为其作出了贡献。

网页截图:Google提供的网页截图工具,该扩展可以轻松截取当前标签页的可见区域,当前网页的指定区域,或是整张网页的页面,截图后,可以利用图片编辑工具编辑图片,然后将编辑后的图片保存为PNG格式的图片文件。

Google Mail Checker:显示Google Mail收件箱中的未读邮件数,点击该按钮还可以打开Gmail收件箱。

RSS Subscription:Google提供的显示网站的RSS地址的扩展,可以在工具栏上显示RSS图标,用于一键订阅。

Feedly:RSS阅读器网站Feedly自带扩展。

AutoPager Chrome:自动翻页扩展,在Google搜索网页或浏览论坛帖子时,可以自动翻页并将每一页的内容接在现有内容的末尾,支持自定义规则。

User-Agent Switcher:设置浏览器的UserAgent,将浏览器伪装成为其他系统的浏览器,例如iPhone等。

Readium:EPUB阅读扩展,支持在Chrome里阅读EPUB书籍。

Proxy SwitchySharp:天朝必备,用于安全上网的扩展,你懂的。

Readability:这个工具其实主要用于科学上网,遇到看不了的文章,点一下,就可以在Readability里面看到。

IE Tab:在Chrome标签页中以IE内核显示网页,平时没啥用,使用中国的网银系统时必备。

Google Webspam Report:Google提供的举报垃圾网页的工具,可以举报Google搜索结果里的垃圾网页,提升Google搜索质量和搜索体验。

TemperMonkey:这是Chrome上的Greasemonkey(油猴),Tampermonkey通过加载第三方的脚本文件,可以让Chrome支持更多UserScript的Chrome 扩展,例如改变页面中的CSS和JS元素,或在网页中增加额外的功能等,高手必备。

常用上网增强类Chrome扩展

[转载]Android系统手机端抓包方法 - 一江水 - 博客园

mikel阅读(1077)

[转载]Android系统手机端抓包方法 – 一江水 – 博客园.

抓包准备

1. Android手机需要先获得root权限。一种是否获得root权限的检验方法:安装并打开终端模拟器(可通过安卓市场等渠道获得)。在终端模拟器界面输入su并回车,若报错则说明未root,若命令提示符从$变#则为rooted;

2. 如果Android手机尚未root,可通过superoneclick或其它方法进行root处理(需要先安装Microsoft .NET Framework)。Superoneclick刷root权限教程:(http://soft.shouji.com.cn/news/501.shtml)

3. 需要先获得 Android SDK

4. 需要获得tcpdump软件,获取地址(http://www.strazzere.com/android/tcpdump)

抓包步骤
1. 将Android手机与电脑USB相连,打开windows命令提示符窗口

2. 将tcpdump程序copy至android手机(该命令前面那个目录文件为本地地址,后面那个目录为目的手机端地址)

C:\android-sdk-windows\platform-tools>adb push c:/tcpdump /data/local/tcpdump

3. 修改tcpdump的权限

C:\android-sdk-windows\platform-tools>adb shell
#chmod 777 /data/local/tcpdump

4. 进入root权限

C:\android-sdk-windows\platform-tools>adb shell
$ su

在运行su指令后,手机终端桌面会出现相应提示信息以确认您对root操作的认可。

5. 运行tcpdump,输入以下命令启动抓包。

/data/local/tcpdump -p -vv -s 0 -w /sdcard/capture.pcap

6. 在手机端执行相应需要进行抓包分析的操作,执行完成后在命令提示符窗口执行Ctrl+C中断抓包进程

7. 将抓包结果复制至本地(前面那个目录为手机端地址,后面那个目录为本地地址)

C:\android-sdk-windows\platform-tools>adb pull /sdcard/capture.pcap c:/

8. 使用Wireshark等工具查看抓包文件capture.pcap

[转载]Android-view listView 每个item里动态添加不定量的控件,Android 自动换行 - helloworld.MR-zz - 博客园

mikel阅读(994)

[转载]【Android-view】listView 每个item里动态添加不定量的控件,Android 自动换行 – helloworld.MR-zz – 博客园.

问题描述:

开发过程中,遇到了在listview里面的每个item都有可能显示图片,并且需要显示的图片的数量不确定,需要自动换行。

如图:第一行显示三张图片,第二行显示四张图片。数量0—正无穷(内存支持的情况下)

解决办法:

最初就是直接从网上找Android自动换行的控件,再此感谢eoe论坛里**dahege **分享的源码。

  dahege eoe论坛原文地址:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=195276

  但是我直接拿来用的时候出现了个问题,就是当有四张图片的时候只显示三行。

具体解决办法如下:

a.修改values下attrs.xml文件

增加一个每行显示多少列的属性,类似gridview

<resources>
    <declare-styleable name="FlowLayout">
        <attr name="horizontalSpacing" format="dimension" />
        <attr name="verticalSpacing" format="dimension" />
        <attr name="numColumns" format="integer" /><!--这个属性为新加的-->
    </declare-styleable>
    <declare-styleable name="FlowLayout_LayoutParams">
        <attr name="layout_breakLine" format="boolean" />
        <attr name="layout_horizontalSpacing" format="dimension" />
    </declare-styleable>
</resources>

  b.修改FlowLayout.java 源文件

    1.在构造方法里得到用户在cml文件里设置的numColumns

public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        try {
            mHorizontalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 0);
            mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 0);
            numColumns = a.getInt(R.styleable.FlowLayout_numColumns, 3);//得到用户在布局文件中设置的没行显示的列数
        } finally {
            a.recycle();
        }
        
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(0xffff0000);
        mPaint.setStrokeWidth(2.0f);
    }

2.修改onMeasure方法,由于对这块还不太了解,所以只是简单的改了一下,测试之后效果是实现了,暂未发现其他问题。应该还有更好的解决方案,希望有人指正。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingRight();
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        boolean growHeight = widthMode != MeasureSpec.UNSPECIFIED;

        int width = 0;
        int height = getPaddingTop();

        int currentWidth = getPaddingLeft(); 
        int currentHeight = 0;               

        boolean breakLine = false;
        boolean newLine = false;
        int spacing = 0;

        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            spacing = mHorizontalSpacing;
            if (lp.horizontalSpacing >= 0) {
                spacing = lp.horizontalSpacing;
            }

            if (growHeight && (i == numColumns||breakLine || currentWidth + child.getMeasuredWidth() > widthSize)) {
//                newLine = true;
                
                height += currentHeight + mVerticalSpacing;
                width = Math.max(width, currentWidth - spacing);
                
                currentHeight = 0;
                currentWidth = getPaddingLeft();
                
            }
//            else {
//                newLine = false;
//            }
            if (i>numColumns&&i%numColumns==0) {//主要修改的是这个判断语句,原版的判断语句是29,38,39,40行的被隐掉的。我自己用原版的判断语句有问题,
                newLine = false;
            }

            lp.x = currentWidth;
            lp.y = height;

            currentWidth += child.getMeasuredWidth() + spacing;
            currentHeight = Math.max(currentHeight, child.getMeasuredHeight());
            
            breakLine = lp.breakLine;
        }

        if (!newLine) {
            height += currentHeight;
            width = Math.max(width, currentWidth - spacing);
        }

        width += getPaddingRight();
        height += getPaddingBottom();

        setMeasuredDimension(resolveSize(width, widthMeasureSpec),
                resolveSize(height, heightMeasureSpec));
    }

至此,就修改完毕了。

具体使用办法如下。

a.在listview的item布局文件中使用framelayout自定义控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:f="http://schemas.android.com/apk/res/你的androidmanifest.xml文件中的package属性值"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
     >

   
        <com.xingyunhudong.view.FlowLayout
            android:id="@+id/flowlaytou"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
           f:numColumns="3"<!--也可以不指定,如果不指定,在FrameLayout的构造函数里,默认取值为3列-->
             >
        </com.xingyunhudong.view.FlowLayout>

        

</LinearLayout>

b.在adapter中设值

public class XXXAdapterextends BaseAdapter {

    private LayoutInflater inflater;
    private List<XXX> xxxList;
    private Context context;

    private ViewGroup.LayoutParams paramsImg, paramsVideo;
    private int sw;

    public HuaTiAdapter(Context context, List<xxx> xxxList) {
        this.xxxList = xxxList;
        this.context = context;
        inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        sw = CommonUtils.getScreenWidth((Activity) context);
        int w = context.getResources().getDimensionPixelSize(
                R.dimen.xxx_img_total_width);
        sw = sw - w;
        paramsImg = new ViewGroup.LayoutParams(sw / 3, sw / 3);
        paramsVideo = new ViewGroup.LayoutParams(sw, 0);//为了图片适配
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return xxxList.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return xxxList.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    class ViewHolder {
        FlowLayout ll;
        int flag;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder = null;
        xxxBean huati = xxxList.get(position);
        if (convertView == null
                || ((ViewHolder) convertView.getTag()).flag != position) {//第一个判断是优化listview加载速度及内存消耗,第二个判断是为了防止图片错位
            holder = new ViewHolder();
            holder.flag = position;
            convertView = inflater.inflate(R.layout.xxx_item_layout, null);
            
            holder.ll = (FlowLayout) convertView.findViewById(R.id.flowlaytou);
            ImageBean video = xxx.getVideoImg();
            if (video != null && video.getUrl() != null
                    && !"".equals(video.getUrl().trim())) {
                paramsVideo.height = video.getHeight() * sw / video.getWidth();
                addVideoView(holder.ll, video.getUrl(), paramsVideo, inflater);
            }
            List<ImageBean> imgList = huati.getImgList();
            if (imgList != null && imgList.size() > 0) {
                for (int i = 0; i < imgList.size(); i++) {
                    addImageView(holder.ll, imgList.get(i).getUrl(), paramsImg,
                            inflater, imgList, i);
                }
            }
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        return convertView;
    }

    private void addVideoView(FlowLayout ll, final String url,
            LayoutParams params, LayoutInflater inflater) {
        ImageView v = (ImageView) inflater.inflate(
                R.layout.yyy_image_layout, null);//这个layout里面就只有一个imageview空间,特别简单
        v.setLayoutParams(params);
        ImageUtil.display(url, v);
        v.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                //这里的点击事件也完全没有问题,不会错位,不会点击失效
            }
        });
        ll.addView(v);

    }

    private void addImageView(FlowLayout ll, String url, LayoutParams params,
            LayoutInflater inflater) {
        // TODO Auto-generated method stub
        ImageView v = (ImageView) inflater.inflate(
                R.layout.weixiu_image_layout, null);
        v.setLayoutParams(params);
        ImageUtil.display(url, v);
        v.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                //这里的点击事件也完全没有问题,不会错位,不会点击失效
 } }); ll.addView(v); } }

至此,就就结束了,希望可以帮到一部分人。