对XSD schema文件中elementFormDefault属性的理解

Schema中的elementFormDefault

elementFormDefault取值:qualified 或者 unqualified

 

http://www.velocityreviews.com/forums/t292290-xsd-what-is-elementformdefault-qualified-for.html这个网站,发现Boris的回答:

All”qualified” elements and attributes are in the targetNamespace of the
schema and all “unqualified” elementsand attributes are in no namespace.
All global elements and attributes are qualified.

 

意思是这样的:

1、在xml中,所有引用xsd的全局的元素都必须加上命名空间的前缀

(例如xmlns:aa=http://www.example.org/classroom,全局元素都得加上aa)。

2、非全局的元素当设置为qualified时,必须添加命名空间的前缀。

3、非全局的元素当设置为unqualified时,不必也不能添加前缀。

 

下面是一个简单的例子:

a.  当设置为unqualified时,user为全局元素(可作为根元素)必须添加前缀,非全局元素

(id,name)不必添加前缀。

b.  当设置为qualified时,所有的元素都必须添加前缀。

其实elementFormDefault的qualified/unqualified还与schema的设计模式有关系,目前常用的有Russian Roll , Salami Slice , Venetian Blind

详细及最佳实践可以参照:http://www.xfront.com/GlobalVersusLocal.html#BestPractice

 

Salami Slice这种schema的设计模式,将所有的元素设置为全局元素,设置不设置elementFormDefault是没有任何意义的,文章的开通说过,所有的全局元素必须添加前缀。

http://www.xfront.com/GlobalVersusLocal.html#BestPractice

上网站中提到的Venetian Blind的优势之一:

UseelementFormDefault to act as a switch for controlling namespaceexposure – if you want element namespaces exposed in instance documents, simplyturn the elementFormDefault switch to “on” (i.e, setelementFormDefault= “qualified”); if you don’t want elementnamespaces exposed in instance documents, simply turn the elementFormDefaultswitch to “off” (i.e., setelementFormDefault=”unqualified”).

 

 

ListView滑动删除 ,仿腾讯QQ

转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/22961279

在CSDN上开了很多大神们的文章,感觉受益良多,也非常欣赏大家的分享态度,所以决定开始写Blog,给大家分享自己的心得。

本来准备在ListView的每个Item的布局上设置一个隐藏的Button,当滑动的时候显示。但是因为每次只要存在一个Button,发现每个Item上的Button相互间不好控制。所以决定继承ListView然后结合PopupWindow。

首先是布局文件:

delete_btn.xml:这里只需要一个Button

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
      <Button 
        android:id="@+id/id_item_btn"
        android:layout_width="60dp"
        android:singleLine="true"
        android:layout_height="wrap_content"
        android:text="删除"
      	android:background="@drawable/d_delete_btn"
      	android:textColor="#ffffff"
      	android:paddingLeft="15dp"
      	android:paddingRight="15dp"
      	android:layout_alignParentRight="true"
      	android:layout_centerVertical="true"
      	android:layout_marginRight="15dp"
        />
</LinearLayout>

主布局文件:activity_main.xml
,ListView的每个Item的样式直接使用了系统的android.R.layout.simple_list_item_1

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.listviewitemslidedeletebtnshow.QQListView
        android:id="@+id/id_listview"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </com.example.listviewitemslidedeletebtnshow.QQListView>

</RelativeLayout>

接下来看看QQListView的实现:

package com.example.listviewitemslidedeletebtnshow;

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;

public class QQListView extends ListView
{

	private static final String TAG = "QQlistView";

	// private static final int VELOCITY_SANP = 200;
	// private VelocityTracker mVelocityTracker;
	/**
	 * 用户滑动的最小距离
	 */
	private int touchSlop;

	/**
	 * 是否响应滑动
	 */
	private boolean isSliding;

	/**
	 * 手指按下时的x坐标
	 */
	private int xDown;
	/**
	 * 手指按下时的y坐标
	 */
	private int yDown;
	/**
	 * 手指移动时的x坐标
	 */
	private int xMove;
	/**
	 * 手指移动时的y坐标
	 */
	private int yMove;

	private LayoutInflater mInflater;

	private PopupWindow mPopupWindow;
	private int mPopupWindowHeight;
	private int mPopupWindowWidth;

	private Button mDelBtn;
	/**
	 * 为删除按钮提供一个回调接口
	 */
	private DelButtonClickListener mListener;

	/**
	 * 当前手指触摸的View
	 */
	private View mCurrentView;

	/**
	 * 当前手指触摸的位置
	 */
	private int mCurrentViewPos;

	/**
	 * 必要的一些初始化
	 * 
	 * @param context
	 * @param attrs
	 */
	public QQListView(Context context, AttributeSet attrs)
	{
		super(context, attrs);

		mInflater = LayoutInflater.from(context);
		touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

		View view = mInflater.inflate(R.layout.delete_btn, null);
		mDelBtn = (Button) view.findViewById(R.id.id_item_btn);
		mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,
				LinearLayout.LayoutParams.WRAP_CONTENT);
		/**
		 * 先调用下measure,否则拿不到宽和高
		 */
		mPopupWindow.getContentView().measure(0, 0);
		mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
		mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev)
	{
		int action = ev.getAction();
		int x = (int) ev.getX();
		int y = (int) ev.getY();
		switch (action)
		{

		case MotionEvent.ACTION_DOWN:
			xDown = x;
			yDown = y;
			/**
			 * 如果当前popupWindow显示,则直接隐藏,然后屏蔽ListView的touch事件的下传
			 */
			if (mPopupWindow.isShowing())
			{
				dismissPopWindow();
				return false;
			}
			// 获得当前手指按下时的item的位置
			mCurrentViewPos = pointToPosition(xDown, yDown);
			// 获得当前手指按下时的item
			View view = getChildAt(mCurrentViewPos - getFirstVisiblePosition());
			mCurrentView = view;
			break;
		case MotionEvent.ACTION_MOVE:
			xMove = x;
			yMove = y;
			int dx = xMove - xDown;
			int dy = yMove - yDown;
			/**
			 * 判断是否是从右到左的滑动
			 */
			if (xMove < xDown && Math.abs(dx) > touchSlop && Math.abs(dy) < touchSlop)
			{
				// Log.e(TAG, "touchslop = " + touchSlop + " , dx = " + dx +
				// " , dy = " + dy);
				isSliding = true;
			}
			break;
		}
		return super.dispatchTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		int action = ev.getAction();
		/**
		 * 如果是从右到左的滑动才相应
		 */
		if (isSliding)
		{
			switch (action)
			{
			case MotionEvent.ACTION_MOVE:

				int[] location = new int[2];
				// 获得当前item的位置x与y
				mCurrentView.getLocationOnScreen(location);
				// 设置popupWindow的动画
				mPopupWindow.setAnimationStyle(R.style.popwindow_delete_btn_anim_style);
				mPopupWindow.update();
				mPopupWindow.showAtLocation(mCurrentView, Gravity.LEFT | Gravity.TOP,
						location[0] + mCurrentView.getWidth(), location[1] + mCurrentView.getHeight() / 2
								- mPopupWindowHeight / 2);
				// 设置删除按钮的回调
				mDelBtn.setOnClickListener(new OnClickListener()
				{
					@Override
					public void onClick(View v)
					{
						if (mListener != null)
						{
							mListener.clickHappend(mCurrentViewPos);
							mPopupWindow.dismiss();
						}
					}
				});
				// Log.e(TAG, "mPopupWindow.getHeight()=" + mPopupWindowHeight);

				break;
			case MotionEvent.ACTION_UP:
				isSliding = false;

			}
			// 相应滑动期间屏幕itemClick事件,避免发生冲突
			return true;
		}

		return super.onTouchEvent(ev);
	}

	/**
	 * 隐藏popupWindow
	 */
	private void dismissPopWindow()
	{
		if (mPopupWindow != null && mPopupWindow.isShowing())
		{
			mPopupWindow.dismiss();
		}
	}

	public void setDelButtonClickListener(DelButtonClickListener listener)
	{
		mListener = listener;
	}

	interface DelButtonClickListener
	{
		public void clickHappend(int position);
	}

}

代码注释写得很详细,简单说一下,在dispatchTouchEvent中设置当前是否响应用户滑动,然后在onTouchEvent中判断是否响应,如果响应则popupWindow以动画的形式展示出来。当然屏幕上如果存在PopupWindow则屏幕ListView的滚动与Item的点击,以及从右到左滑动时屏幕Item的click事件。

接下来是MainActivity.java,这里代码很简单不做介绍了。

package com.example.listviewitemslidedeletebtnshow;

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

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Toast;

import com.example.listviewitemslidedeletebtnshow.QQListView.DelButtonClickListener;

public class MainActivity extends Activity
{
	private QQListView mListView;
	private ArrayAdapter<String> mAdapter;
	private List<String> mDatas;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mListView = (QQListView) findViewById(R.id.id_listview);
		// 不要直接Arrays.asList
		mDatas = new ArrayList<String>(Arrays.asList("HelloWorld", "Welcome", "Java", "Android", "Servlet", "Struts",
				"Hibernate", "Spring", "HTML5", "Javascript", "Lucene"));
		mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mDatas);
		mListView.setAdapter(mAdapter);

		mListView.setDelButtonClickListener(new DelButtonClickListener()
		{
			@Override
			public void clickHappend(final int position)
			{
				Toast.makeText(MainActivity.this, position + " : " + mAdapter.getItem(position), 1).show();
				mAdapter.remove(mAdapter.getItem(position));
			}
		});

		mListView.setOnItemClickListener(new OnItemClickListener()
		{
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id)
			{
				Toast.makeText(MainActivity.this, position + " : " + mAdapter.getItem(position), 1).show();
			}
		});
	}
}

效果图如下:楼主使用asm.jar以及gifcamera截的gif,由于button的动画很短感觉截图效果很卡不流畅,大家有什么好的截图,还望推荐。有兴趣的还是下载源码看看效果i。

源码下载,点击这里




Android访问服务器(TOMCAT)乱码引发的问题

1、浏览器往服务器发送的请求主要可分为2种:get、post;delete、head等不赘述。

GET方式:

从浏览器上直接敲地址,最大特点就是参数直接跟在地址后面。

POST方式:表单提交等。

 

2、访问过程:

浏览器中输入地址 –> 浏览器对中文进行编码 –>发送到服务器 ->服务器进行解码

如何浏览器编码和服务器解码用的字符集不一致就会发生乱码问题。

3、乱码的解决

默认浏览器使用UTF-8编码(IE默认GBK当然可以通过meta标签设置)

服务器(Tomcat)默认使用iso-8859-1解码。Iso-8859-1是不支持中文的,也就是说不做处理,中文是一定乱码的。

 

POST方式解决:

比如表单提交,在Servlet或者Filter中设置request.setCharacterEncoding(“UTF-8”);就能很好的解决。

 

GET方式:单纯设置request.setCharacterEncoding(“UTF-8”);是没有用的,所以我们把默认的iso-8859-1编码改成UTF-8,在TOMCAT的配置文件的server.xml中更改:

  <Connector port=”8080″protocol=”HTTP/1.1″

              connectionTimeout=”20000″

              redirectPort=”8443″ URIEncoding=”UTF-8″ />

添加URIEncoding=UTF-8

 

 

4、Android中访问:

 

POST方式,通过HttpUrlConnection封装POST请求【具体实现百度】,POST方式参数是需要打包发送的,不是直接跟在url后面。例如:http://www.demo.com?p=123这不是post传输参数。

 

GET方式,通过HttpUrlConnection可以很简单的实现,但是会发现,很容易出现乱码问题。

首先:添加URIEncoding=UTF-8工作完成,还是会发送乱码。

这是因为,上面的访问过程提到浏览器对中文进行编码,这里我们直接发送请求,并没有编码这个过程,所以我们需要自己手动编码,即:

String name =URLEncoder.encode(“中国万岁”,”UTF-8″);

这样我们就模拟了浏览器的编码,然后发送至服务器,服务器解码也改成UTF-8了,于是又可以快乐的玩耍了。

 

 

利用Theme自定义Activity间的切换动画

转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/22990643

大多Android系统默认Activity间的动画切换效果为,右边滑入,左边滑出;有时候我们的需求可能是要求所有Activity的切换为淡入淡出的效果,这时候就可能需要改变一下默认的切换风格。

下面开始实现:

首先在res文件夹下建立anim文件夹,然后在里面建立fade_in.xml和fade_out.xml两个动画资源

fade_in.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

fade_out
.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" />

然后在values文件夹下的styles.xml中的resources标签内写:

 <style name="Anim_fade" parent="android:Theme.NoTitleBar">
        <item name="android:windowAnimationStyle">@style/fade</item>
    </style>

    <style name="fade" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/fade_in</item>
        <item name="android:activityOpenExitAnimation">@anim/fade_out</item>
        <item name="android:activityCloseEnterAnimation">@anim/fade_in</item>
        <item name="android:activityCloseExitAnimation">@anim/fade_out</item>
    </style>


最后一步在AndroidManifest.xml中的Activity的声明上加入android:theme=”@style/Anim_fade”

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.customanimationforactivity"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="10" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.NoTitleBar" >
        <activity
            android:name="com.example.customanimationforactivity.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/Anim_fade" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".AppActivity" android:theme="@style/Anim_fade" >
        </activity>
    </application>

</manifest>

贴下Splash Activity的代码:

package com.example.customanimationforactivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;

public class MainActivity extends Activity
{

	private Handler handler = new Handler();

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		handler.postDelayed(new Runnable()
		{
			@Override
			public void run()
			{
				Intent intent = new Intent(MainActivity.this, AppActivity.class);
				startActivity(intent);
				finish();
			}
		}, 1000);

	}

}

源码下载,点击这里

Android使用Activity用作弹出式对话框

转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/23116115

Android中可用于实现对话框的有Dialog,PopupWindow,Activity。

下面简单介绍下,Dialog比较方便,但是显示位置比较固定,有时不能满足我们的需求。

例子:(消除了背景)

<resources>
    <style name="dialog" parent="@android:style/Theme.Dialog">
         <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:background">@android:color/black</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>
</resources>
 Dialog dialog = new Dialog(SetActivity.this, R.style.dialog);
                    dialog.setContentView(R.layout.test);
                    dialog.show();


PopupWindow可以通过showAtLocation设置显示位置,也可以通过ShowAsDropDown显示在某个View的相对位置,基本能满足我们的需求了。

下面介绍使用Activity作为弹出对话框,个人觉得好处有以下:

        1、显示位置的设置,直接就是一个layout.xml随心所遇的感觉,可以实现在任何位置。

        2、对对话框内控件的事件的处理,都独立出来在一个类中,调用的地方仅需要startActivity()就可以,这样可以使代码结构上更加清晰。

下面我们实现这个一个例子:

1、Activity的布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    >

    <LinearLayout
        android:onClick="tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="46dp"
        android:background="@drawable/title_function_bg"
        android:orientation="vertical" >

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:drawableLeft="@drawable/mm_title_btn_compose_normal"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="发起聊天"
            android:textColor="#fff"
            android:textSize="20sp" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:drawableLeft="@drawable/mm_title_btn_keyboard_normal"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="登录网页版"
            android:textColor="#fff"
            android:textSize="20sp" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:drawableLeft="@drawable/mm_title_btn_qrcode_normal"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="扫一扫"
            android:textColor="#fff"
            android:textSize="20sp" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginTop="10dp"
            android:drawableLeft="@drawable/mm_title_btn_receiver_normal"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:text="听筒模式"
            android:textColor="#fff"
            android:textSize="20sp" />
    </LinearLayout>

</RelativeLayout>

2、在style.xml中定义一个theme(背景透明,无标题,动画效果),一般Activity默认动画效果右进右出,我们可以覆盖。

   <style name="MyDialogTopRight">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowAnimationStyle">@style/Anim_scale</item>
    </style>

动画效果:

    <style name="Anim_scale" parent="@android:style/Animation.Activity">
        <item name="android:activityOpenEnterAnimation">@anim/scale_in</item>
        <item name="android:activityOpenExitAnimation">@anim/scale_out</item>
         <item name="android:activityCloseEnterAnimation">@anim/scale_in</item>
        <item name="android:activityCloseExitAnimation">@anim/scale_out</item>
    </style>

3、在需要使用的地方直接startActity()就可以了

Intent intent = new Intent(context, MainWeixinTitleRightActivity.class);
		startActivity(intent);

最终效果:

源码下载,点击这里

 

掘金是一个高质量的技术社区,从 RxJava 到 React Native,性能优化到优秀开源库,让你不错过 Android 开发的每一个技术干货。扫描图片二维码识别或者各大应用市场搜索 「掘金」,技术干货尽在掌握中。

Java爬虫,信息抓取的实现

转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/23272657

今天公司有个需求,需要做一些指定网站查询后的数据的抓取,于是花了点时间写了个demo供演示使用。

思想很简单:就是通过Java访问的链接,然后拿到html字符串,然后就是解析链接等需要的数据。

技术上使用Jsoup方便页面的解析,当然Jsoup很方便,也很简单,一行代码就能知道怎么用了:

 Document doc = Jsoup.connect("http://www.oschina.net/") 
  .data("query", "Java")   // 请求参数
  .userAgent("I ’ m jsoup") // 设置 User-Agent 
  .cookie("auth", "token") // 设置 cookie 
  .timeout(3000)           // 设置连接超时时间
  .post();                 // 使用 POST 方法访问 URL 

下面介绍整个实现过程:

1、分析需要解析的页面:

网址:http://www1.sxcredit.gov.cn/public/infocomquery.do?method=publicIndexQuery

页面:

先在这个页面上做一次查询:观察下请求的url,参数,method等。

这里我们使用chrome内置的开发者工具(快捷键F12),下面是查询的结果:

我们可以看到url,method,以及参数。知道了如何或者查询的URL,下面就开始代码了,为了重用与扩展,我定义了几个类:

1、Rule.java用于指定查询url,method,params等

package com.zhy.spider.rule;

/**
 * 规则类
 * 
 * @author zhy
 * 
 */
public class Rule
{
	/**
	 * 链接
	 */
	private String url;

	/**
	 * 参数集合
	 */
	private String[] params;
	/**
	 * 参数对应的值
	 */
	private String[] values;

	/**
	 * 对返回的HTML,第一次过滤所用的标签,请先设置type
	 */
	private String resultTagName;

	/**
	 * CLASS / ID / SELECTION
	 * 设置resultTagName的类型,默认为ID 
	 */
	private int type = ID ;
	
	/**
	 *GET / POST
	 * 请求的类型,默认GET
	 */
	private int requestMoethod = GET ; 
	
	public final static int GET = 0 ;
	public final static int POST = 1 ;
	

	public final static int CLASS = 0;
	public final static int ID = 1;
	public final static int SELECTION = 2;

	public Rule()
	{
	}

	
	public Rule(String url, String[] params, String[] values,
			String resultTagName, int type, int requestMoethod)
	{
		super();
		this.url = url;
		this.params = params;
		this.values = values;
		this.resultTagName = resultTagName;
		this.type = type;
		this.requestMoethod = requestMoethod;
	}

	public String getUrl()
	{
		return url;
	}

	public void setUrl(String url)
	{
		this.url = url;
	}

	public String[] getParams()
	{
		return params;
	}

	public void setParams(String[] params)
	{
		this.params = params;
	}

	public String[] getValues()
	{
		return values;
	}

	public void setValues(String[] values)
	{
		this.values = values;
	}

	public String getResultTagName()
	{
		return resultTagName;
	}

	public void setResultTagName(String resultTagName)
	{
		this.resultTagName = resultTagName;
	}

	public int getType()
	{
		return type;
	}

	public void setType(int type)
	{
		this.type = type;
	}

	public int getRequestMoethod()
	{
		return requestMoethod;
	}

	public void setRequestMoethod(int requestMoethod)
	{
		this.requestMoethod = requestMoethod;
	}

}

简单说一下:这个规则类定义了我们查询过程中需要的所有信息,方便我们的扩展,以及代码的重用,我们不可能针对每个需要抓取的网站写一套代码。

2、需要的数据对象,目前只需要链接,LinkTypeData.java

package com.zhy.spider.bean;

public class LinkTypeData
{
	private int id;
	/**
	 * 链接的地址
	 */
	private String linkHref;
	/**
	 * 链接的标题
	 */
	private String linkText;
	/**
	 * 摘要
	 */
	private String summary;
	/**
	 * 内容
	 */
	private String content;
	public int getId()
	{
		return id;
	}
	public void setId(int id)
	{
		this.id = id;
	}
	public String getLinkHref()
	{
		return linkHref;
	}
	public void setLinkHref(String linkHref)
	{
		this.linkHref = linkHref;
	}
	public String getLinkText()
	{
		return linkText;
	}
	public void setLinkText(String linkText)
	{
		this.linkText = linkText;
	}
	public String getSummary()
	{
		return summary;
	}
	public void setSummary(String summary)
	{
		this.summary = summary;
	}
	public String getContent()
	{
		return content;
	}
	public void setContent(String content)
	{
		this.content = content;
	}
}

3、核心的查询类:ExtractService.java

package com.zhy.spider.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.plaf.TextUI;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.zhy.spider.bean.LinkTypeData;
import com.zhy.spider.rule.Rule;
import com.zhy.spider.rule.RuleException;
import com.zhy.spider.util.TextUtil;

/**
 * 
 * @author zhy
 * 
 */
public class ExtractService
{
	/**
	 * @param rule
	 * @return
	 */
	public static List<LinkTypeData> extract(Rule rule)
	{

		// 进行对rule的必要校验
		validateRule(rule);

		List<LinkTypeData> datas = new ArrayList<LinkTypeData>();
		LinkTypeData data = null;
		try
		{
			/**
			 * 解析rule
			 */
			String url = rule.getUrl();
			String[] params = rule.getParams();
			String[] values = rule.getValues();
			String resultTagName = rule.getResultTagName();
			int type = rule.getType();
			int requestType = rule.getRequestMoethod();

			Connection conn = Jsoup.connect(url);
			// 设置查询参数

			if (params != null)
			{
				for (int i = 0; i < params.length; i++)
				{
					conn.data(params[i], values[i]);
				}
			}

			// 设置请求类型
			Document doc = null;
			switch (requestType)
			{
			case Rule.GET:
				doc = conn.timeout(100000).get();
				break;
			case Rule.POST:
				doc = conn.timeout(100000).post();
				break;
			}

			//处理返回数据
			Elements results = new Elements();
			switch (type)
			{
			case Rule.CLASS:
				results = doc.getElementsByClass(resultTagName);
				break;
			case Rule.ID:
				Element result = doc.getElementById(resultTagName);
				results.add(result);
				break;
			case Rule.SELECTION:
				results = doc.select(resultTagName);
				break;
			default:
				//当resultTagName为空时默认去body标签
				if (TextUtil.isEmpty(resultTagName))
				{
					results = doc.getElementsByTag("body");
				}
			}

			for (Element result : results)
			{
				Elements links = result.getElementsByTag("a");

				for (Element link : links)
				{
					//必要的筛选
					String linkHref = link.attr("href");
					String linkText = link.text();

					data = new LinkTypeData();
					data.setLinkHref(linkHref);
					data.setLinkText(linkText);

					datas.add(data);
				}
			}

		} catch (IOException e)
		{
			e.printStackTrace();
		}

		return datas;
	}

	/**
	 * 对传入的参数进行必要的校验
	 */
	private static void validateRule(Rule rule)
	{
		String url = rule.getUrl();
		if (TextUtil.isEmpty(url))
		{
			throw new RuleException("url不能为空!");
		}
		if (!url.startsWith("http://"))
		{
			throw new RuleException("url的格式不正确!");
		}

		if (rule.getParams() != null && rule.getValues() != null)
		{
			if (rule.getParams().length != rule.getValues().length)
			{
				throw new RuleException("参数的键值对个数不匹配!");
			}
		}

	}


}

4、里面用了一个异常类:RuleException.java

package com.zhy.spider.rule;

public class RuleException extends RuntimeException
{

	public RuleException()
	{
		super();
		// TODO Auto-generated constructor stub
	}

	public RuleException(String message, Throwable cause)
	{
		super(message, cause);
		// TODO Auto-generated constructor stub
	}

	public RuleException(String message)
	{
		super(message);
		// TODO Auto-generated constructor stub
	}

	public RuleException(Throwable cause)
	{
		super(cause);
		// TODO Auto-generated constructor stub
	}

}

5、最后是测试了:这里使用了两个网站进行测试,采用了不同的规则,具体看代码吧

package com.zhy.spider.test;

import java.util.List;

import com.zhy.spider.bean.LinkTypeData;
import com.zhy.spider.core.ExtractService;
import com.zhy.spider.rule.Rule;

public class Test
{
	@org.junit.Test
	public void getDatasByClass()
	{
		Rule rule = new Rule(
				"http://www1.sxcredit.gov.cn/public/infocomquery.do?method=publicIndexQuery",
		new String[] { "query.enterprisename","query.registationnumber" }, new String[] { "兴网","" },
				"cont_right", Rule.CLASS, Rule.POST);
		List<LinkTypeData> extracts = ExtractService.extract(rule);
		printf(extracts);
	}

	@org.junit.Test
	public void getDatasByCssQuery()
	{
		Rule rule = new Rule("http://www.11315.com/search",
				new String[] { "name" }, new String[] { "兴网" },
				"div.g-mn div.con-model", Rule.SELECTION, Rule.GET);
		List<LinkTypeData> extracts = ExtractService.extract(rule);
		printf(extracts);
	}

	public void printf(List<LinkTypeData> datas)
	{
		for (LinkTypeData data : datas)
		{
			System.out.println(data.getLinkText());
			System.out.println(data.getLinkHref());
			System.out.println("***********************************");
		}

	}
}

输出结果:

深圳市网兴科技有限公司
http://14603257.11315.com
***********************************
荆州市兴网公路物资有限公司
http://05155980.11315.com
***********************************
西安市全兴网吧
#
***********************************
子长县新兴网城
#
***********************************
陕西同兴网络信息有限责任公司第三分公司
#
***********************************
西安高兴网络科技有限公司
#
***********************************
陕西同兴网络信息有限责任公司西安分公司
#
***********************************

最后使用一个Baidu新闻来测试我们的代码:说明我们的代码是通用的。

         /**
	 * 使用百度新闻,只设置url和关键字与返回类型
	 */
	@org.junit.Test
	public void getDatasByCssQueryUserBaidu()
	{
		Rule rule = new Rule("http://news.baidu.com/ns",
				new String[] { "word" }, new String[] { "支付宝" },
				null, -1, Rule.GET);
		List<LinkTypeData> extracts = ExtractService.extract(rule);
		printf(extracts);
	}

我们只设置了链接、关键字、和请求类型,不设置具体的筛选条件。

结果:有一定的垃圾数据是肯定的,但是需要的数据肯定也抓取出来了。我们可以设置Rule.SECTION,以及筛选条件进一步的限制。

按时间排序
/ns?word=支付宝&ie=utf-8&bs=支付宝&sr=0&cl=2&rn=20&tn=news&ct=0&clk=sortbytime
***********************************
x
javascript:void(0)
***********************************
支付宝将联合多方共建安全基金 首批投入4000万
http://finance.ifeng.com/a/20140409/12081871_0.shtml
***********************************
7条相同新闻
/ns?word=%E6%94%AF%E4%BB%98%E5%AE%9D+cont:2465146414%7C697779368%7C3832159921&same=7&cl=1&tn=news&rn=30&fm=sd
***********************************
百度快照
http://cache.baidu.com/c?m=9d78d513d9d437ab4f9e91697d1cc0161d4381132ba7d3020cd0870fd33a541b0120a1ac26510d19879e20345dfe1e4bea876d26605f75a09bbfd91782a6c1352f8a2432721a844a0fd019adc1452fc423875d9dad0ee7cdb168d5f18c&p=c96ec64ad48b2def49bd9b780b64&newp=c4769a4790934ea95ea28e281c4092695912c10e3dd796&user=baidu&fm=sc&query=%D6%A7%B8%B6%B1%A6&qid=a400f3660007a6c5&p1=1
***********************************
OpenSSL漏洞涉及众多网站 支付宝称暂无数据泄露
http://tech.ifeng.com/internet/detail_2014_04/09/35590390_0.shtml
***********************************
26条相同新闻
/ns?word=%E6%94%AF%E4%BB%98%E5%AE%9D+cont:3869124100&same=26&cl=1&tn=news&rn=30&fm=sd
***********************************
百度快照
http://cache.baidu.com/c?m=9f65cb4a8c8507ed4fece7631050803743438014678387492ac3933fc239045c1c3aa5ec677e4742ce932b2152f4174bed843670340537b0efca8e57dfb08f29288f2c367117845615a71bb8cb31649b66cf04fdea44a7ecff25e5aac5a0da4323c044757e97f1fb4d7017dd1cf4&p=8b2a970d95df11a05aa4c32013&newp=9e39c64ad4dd50fa40bd9b7c5253d8304503c52251d5ce042acc&user=baidu&fm=sc&query=%D6%A7%B8%B6%B1%A6&qid=a400f3660007a6c5&p1=2
***********************************
雅虎日本6月起开始支持支付宝付款
http://www.techweb.com.cn/ucweb/news/id/2025843
***********************************

如果有什么不足,可以指出;如果觉得对你有用,顶一下~~哈哈

源码下载,点击这里。

Android Java汉字转拼音总结

转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/23187701    

  开发过程中有时候会遇到使用拼音模糊搜索等功能(典型的就是Android通讯录了),这时就需要汉字能够转化为拼音。

1、Android系统的通讯录也实现了拼音搜索,所以我们可以偷个懒,直接拿Android系统中的实现类来实现这个功能。

sdk\sources\android-2.3.3-gingerbread-src\com\android\internal\util\HanziToPinyin.java

该类我们无法直接获取实例或者反射访问,不过我们可以直接把源码拷入自己的项目中,然后使用。

概览:

简单看一下,就提供了一个get(Sting)方法供使用。

使用方式:

public void testHanziToPinyin()
	{
		ArrayList<Token> list = HanziToPinyin.getInstance().get(
		"单赵 钱 孙 李 周 吴 郑 王冯 陈 褚 卫 蒋 沈 韩 杨 朱");
		for (Token token : list)
		{
			System.out.print(token.source + " , " + token.target + " , " + token.type);
			System.out.println();
		}
	}

输出结果:

简单解释一下:HanziToPinyin是一个单例,获得该类实例后,get方法传入一个字符串,然后会对每个字符串的每个字符进行解析,解析结果存入Token中,

token.source=源 ;token.target=结果,token.type=2(也就是  public static final int PINYIN = 2; 封装的常量)

2、使用Pinyin4j

首先去http://pinyin4j.sourceforge.net/   下载一个jar包。

主要的几个类:

PinyinHelper 调用方法的核心类

HanyuPinyinOutputFormat 输出格式,设置HanyuPinyinCaseType(大小写),HanyuPinyinToneType(声调的方式),HanyuPinyinVCharType(V的输出方式)

使用方式:

String str = "单赵钱孙李周吴郑王冯陈褚卫abc";
		HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
		format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
		format.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER);
		for(int i = 0 ; i < str.length() ; i++)
		{
			char c = str.charAt(i);
			
			String[] vals = PinyinHelper.toHanyuPinyinStringArray(c, format);
			System.out.print(Arrays.toString(vals));
		}

输出结果:对于非汉字输出的是返回null

[DAN1, CHAN2, SHAN4][ZHAO4][QIAN2][SUN1][LI3][ZHOU1][WU2][ZHENG4][WANG2, WANG4][FENG2, PING2][CHEN2][CHU3, ZHU3][WEI4]nullnullnull

附上一张官网的图,关于各种格式组合的结果:

Some combinations of these three output formats are forbidden. For example, ‘吕’

LOWERCASE Combination WITH_U_AND_COLON WITH_V WITH_U_UNICODE WITH_TONE_NUMBER lu:3 lv3 lü3 WITHOUT_TONE lu: lv lü WITH_TONE_MARK
Exception
Exception

UPPERCASE Combination WITH_U_AND_COLON WITH_V WITH_U_UNICODE WITH_TONE_NUMBER LU:3 LV3 LÜ3 WITHOUT_TONE LU: LV LÜ WITH_TONE_MARK
Exception
Exception


我附上:HanziToPinyin.java以及Pinyin4J.jar供找不到源码或者无法正常下载的亲们下载,下载地址:http://download.csdn.net/detail/lmj623565791/7161713。

Android 仿Win8的metro的UI界面(上)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23441455

昨晚没事手机下载了一些APP,发现现在仿win8的主界面越来越多,在大家见惯了类GridView或者类Tab后,给人一种耳目一新的感觉。今天在eoe上偶然发现已经有人实现了这个功能的源码(地址:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=327557),马上下载跑了一下,效果很炫,但是有些bug,比如点击速度特别快时图像会被放大,以及点击时会触发两次点击事件。

本例子基于eoe中这位大神的实现,做了一些简化,和bug的修复。

效果:

首先普及一个小知识点:

我们在项目中有时候需要一个缓慢的梯度数据,例如:控件的宽度以一定的比例增加,然后以相同的比例还原到原来的长度。

package com.zhy._01;

public class Test2
{
	public static void main(String[] args)
	{
		float val = 1; 
		float s = 0.85f;
		int i = 0;
		s = (float) Math.sqrt(1 / s);
                System.out.println(val);
		while (i < 5)
		{
			val = val *s ;
			System.out.println(val);
			i++;
		}
		 s = 0.85f;
		i = 0;
		s = (float) Math.sqrt(s);
		while (i < 5)
		{
			val = val *s ;
			System.out.println(val);
			i++;
		}

	}
}

输出结果:

1.0
1.0846523
1.1764706
1.2760615
1.384083
1.5012488
1.384083
1.2760615
1.1764706
1.0846523
1.0

很完美吧,基本是个对称的梯度数据,梯度的幅度由代码中的s觉得,越接近1幅度越小,反之则反之。

好了下面开始代码:

1、布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bkg_img_default"
    android:gravity="center"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <com.ljp.ani01.MyImageView
                    android:id="@+id/c_joke"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:scaleType="matrix"
                    android:src="@drawable/left_top" />

                <com.ljp.ani01.MyImageView
                    android:id="@+id/c_idea"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="2dp"
                    android:scaleType="matrix"
                    android:src="@drawable/left_bottom" />
            </LinearLayout>

            <com.ljp.ani01.MyImageView
                android:id="@+id/c_constellation"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="2dp"
                android:scaleType="matrix"
                android:src="@drawable/right" />
        </LinearLayout>

        <com.ljp.ani01.MyImageView
            android:id="@+id/c_recommend"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="2dp"
            android:scaleType="matrix"
            android:src="@drawable/bottom" />
    </LinearLayout>

</LinearLayout>

布局文件,完成了上面效果图的静态效果,如果你不需要添加点击动画,或者只需要很简单的点击效果,那么就已经完成这样的菜单的编写,再添加个backgroud自定义下点击效果就好了。当然,我们这里有个比较柔和的点击动画,有自定义的ImageView完成。

2、MyImageView.java

package com.ljp.ani01;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;

public class MyImageView extends ImageView
{

	private static final String TAG = "MyImageView";

	private static final int SCALE_REDUCE_INIT = 0;
	private static final int SCALING = 1;
	private static final int SCALE_ADD_INIT = 6;

	/**
	 * 控件的宽
	 */
	private int mWidth;
	/**
	 * 控件的高
	 */
	private int mHeight;
	/**
	 * 控件的宽1/2
	 */
	private int mCenterWidth;
	/**
	 * 控件的高 1/2
	 */
	private int mCenterHeight;
	/**
	 * 设置一个缩放的常量
	 */
	private float mMinScale = 0.85f;
	/**
	 * 缩放是否结束
	 */
	private boolean isFinish = true;

	public MyImageView(Context context)
	{
		this(context, null);
	}

	public MyImageView(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);
	}

	public MyImageView(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
	}

	/**
	 * 必要的初始化
	 */
	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom)
	{
		super.onLayout(changed, left, top, right, bottom);
		if (changed)
		{
			mWidth = getWidth() - getPaddingLeft() - getPaddingRight();
			mHeight = getHeight() - getPaddingTop() - getPaddingBottom();

			mCenterWidth = mWidth / 2;
			mCenterHeight = mHeight / 2;

			Drawable drawable = getDrawable();
			BitmapDrawable bd = (BitmapDrawable) drawable;
			bd.setAntiAlias(true);
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		switch (event.getAction())
		{
		case MotionEvent.ACTION_DOWN:
			float X = event.getX();
			float Y = event.getY();
			mScaleHandler.sendEmptyMessage(SCALE_REDUCE_INIT);
			break;
		case MotionEvent.ACTION_UP:
			mScaleHandler.sendEmptyMessage(SCALE_ADD_INIT);
			break;
		}
		return true;
	}

	/**
	 * 控制缩放的Handler
	 */
	private Handler mScaleHandler = new Handler()
	{
		private Matrix matrix = new Matrix();
		private int count = 0;
		private float s;
		/**
		 * 是否已经调用了点击事件
		 */
		private boolean isClicked;

		public void handleMessage(android.os.Message msg)
		{
			matrix.set(getImageMatrix());
			switch (msg.what)
			{
			case SCALE_REDUCE_INIT:
				if (!isFinish)
				{
					mScaleHandler.sendEmptyMessage(SCALE_REDUCE_INIT);
				} else
				{
					isFinish = false;
					count = 0;
					s = (float) Math.sqrt(Math.sqrt(mMinScale));
					beginScale(matrix, s);
					mScaleHandler.sendEmptyMessage(SCALING);
				}
				break;
			case SCALING:
				beginScale(matrix, s);
				if (count < 4)
				{
					mScaleHandler.sendEmptyMessage(SCALING);
				} else
				{
					isFinish = true;
					if (MyImageView.this.mOnViewClickListener != null && !isClicked)
					{
						isClicked = true;
						MyImageView.this.mOnViewClickListener.onViewClick(MyImageView.this);
					} else
					{
						isClicked = false;
					}
				}
				count++;

				break;
			case 6:
				if (!isFinish)
				{
					mScaleHandler.sendEmptyMessage(SCALE_ADD_INIT);
				} else
				{
					isFinish = false;
					count = 0;
					s = (float) Math.sqrt(Math.sqrt(1.0f / mMinScale));
					beginScale(matrix, s);
					mScaleHandler.sendEmptyMessage(SCALING);
				}
				break;
			}
		}
	};

	protected void sleep(int i)
	{
		try
		{
			Thread.sleep(i);
		} catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * 缩放
	 * 
	 * @param matrix
	 * @param scale
	 */
	private synchronized void beginScale(Matrix matrix, float scale)
	{
		matrix.postScale(scale, scale, mCenterWidth, mCenterHeight);
		setImageMatrix(matrix);
	}

	/**
	 * 回调接口
	 */
	private OnViewClickListener mOnViewClickListener;

	public void setOnClickIntent(OnViewClickListener onViewClickListener)
	{
		this.mOnViewClickListener = onViewClickListener;
	}

	public interface OnViewClickListener
	{
		void onViewClick(MyImageView view);
	}

}

代码不算复杂,主要就是对onTouchEvent的Action_Down和Action_Up的监听,然后通过Handler结合matrix完成缩放的效果。这里简单说一个mScaleHandler里面代码的逻辑,当检测到ACTION_DOWN事件,会判断当前缩放是否完成,如果完成了则添加缩小的效果,如果没有,则一直检测。ACTION_UP也是同样的过程。缩放的梯度就用到了文章开始介绍的小知识点。

有人会觉得使用Handler比较麻烦,这里一直使用Handler.sendMsg的原因是,利用了这个消息队列,队列先进先出,保证动画效果的流畅。因为ACTION_DOWN_与ACTION_UP一瞬点完成的,其实动画还在进行。如果你在onTouchEvent中用while集合sleep完成动画,会出现卡死,监听不到Up事件等问题。

3、主Activity

package com.ljp.ani01;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class TestRolateAnimActivity extends Activity
{
	MyImageView joke;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		joke = (MyImageView) findViewById(R.id.c_joke);
		joke.setOnClickIntent(new MyImageView.OnViewClickListener()
		{

			@Override
			public void onViewClick(MyImageView view)
			{
				Toast.makeText(TestRolateAnimActivity.this, "Joke", 1000).show();
			}
		});
	}
	
	
}


利用提供的回调接口注册了点击事件。这里说明一下,现在为ImageView设置OnClickLIstener是没有作用的,因为自定义的ImageView的onTouchEvent直接返回了true,不会往下执行click事件,如果你希望通过OnClickLIstener进行注册,你可以把ontouchevent里面返回值改成super.ontouchevent(event),并且需要将ImageView的clickable设置为true。这些都是Ontouch事件的传播机制,不了解的google下,还是很有必要的。

如果你觉得这篇文章对你有用,可以顶一个~~

源码下载点击此处

Android 省市县 三级联动(android-wheel的使用)

转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/23382805

今天没事跟群里面侃大山,有个哥们说道Android Wheel这个控件,以为是Andriod内置的控件,google一把,发现是个github上的一个控件。

下载地址:https://code.google.com/p/android-wheel/    发现很适合做省市县三级联动就做了一个。

先看下效果图:

1、首先导入github上的wheel项目

2、新建个项目,然后选择记得右键->Properties->Android中将wheel添加为lib:

上面两个步骤是导入所有开源项目的过程了。

3、下面开始代码的编写:首先是省市区的json文件,放置在asserts的city.json中:

大概的格式先了解一下,一会代码会根据这样的格式解析

{"citylist":
	[{"p":"河北",
	  "c":[{"n":"石家庄",
	  "a":[{"s":"长安区"},{"s":"桥东区"},{"s":"鹿泉市"}]
	}]
}

4、布局文件,比较简单就3个WheelView分别代表省,市,县,还有一个按钮:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="10dp"
        android:text="请选择城市"
        android:textColor="#ffffff"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/layout_bg"
        android:orientation="horizontal" >

        <kankan.wheel.widget.WheelView
            android:id="@+id/id_province"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" >
        </kankan.wheel.widget.WheelView>

        <kankan.wheel.widget.WheelView
            android:id="@+id/id_city"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" >
        </kankan.wheel.widget.WheelView>

        <kankan.wheel.widget.WheelView
            android:id="@+id/id_area"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" >
        </kankan.wheel.widget.WheelView>
    </LinearLayout>
    
    <Button 
		android:onClick="showChoose"
		android:layout_gravity="right"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="确定"
        />
    

</LinearLayout>

5、Activity的编写:注释相当详细,节不赘述了。

package com.example.wheel_province;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import kankan.wheel.widget.OnWheelChangedListener;
import kankan.wheel.widget.WheelView;
import kankan.wheel.widget.adapters.ArrayWheelAdapter;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

/**
 * 
 * @author zhy
 * 
 */
public class CitiesActivity extends Activity implements OnWheelChangedListener
{
	/**
	 * 把全国的省市区的信息以json的格式保存,解析完成后赋值为null
	 */
	private JSONObject mJsonObj;
	/**
	 * 省的WheelView控件
	 */
	private WheelView mProvince;
	/**
	 * 市的WheelView控件
	 */
	private WheelView mCity;
	/**
	 * 区的WheelView控件
	 */
	private WheelView mArea;

	/**
	 * 所有省
	 */
	private String[] mProvinceDatas;
	/**
	 * key - 省 value - 市s
	 */
	private Map<String, String[]> mCitisDatasMap = new HashMap<String, String[]>();
	/**
	 * key - 市 values - 区s
	 */
	private Map<String, String[]> mAreaDatasMap = new HashMap<String, String[]>();

	/**
	 * 当前省的名称
	 */
	private String mCurrentProviceName;
	/**
	 * 当前市的名称
	 */
	private String mCurrentCityName;
	/**
	 * 当前区的名称
	 */
	private String mCurrentAreaName ="";

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.citys);

		initJsonData();

		mProvince = (WheelView) findViewById(R.id.id_province);
		mCity = (WheelView) findViewById(R.id.id_city);
		mArea = (WheelView) findViewById(R.id.id_area);

		initDatas();

		mProvince.setViewAdapter(new ArrayWheelAdapter<String>(this, mProvinceDatas));
		// 添加change事件
		mProvince.addChangingListener(this);
		// 添加change事件
		mCity.addChangingListener(this);
		// 添加change事件
		mArea.addChangingListener(this);

		mProvince.setVisibleItems(5);
		mCity.setVisibleItems(5);
		mArea.setVisibleItems(5);
		updateCities();
		updateAreas();

	}

	/**
	 * 根据当前的市,更新区WheelView的信息
	 */
	private void updateAreas()
	{
		int pCurrent = mCity.getCurrentItem();
		mCurrentCityName = mCitisDatasMap.get(mCurrentProviceName)[pCurrent];
		String[] areas = mAreaDatasMap.get(mCurrentCityName);

		if (areas == null)
		{
			areas = new String[] { "" };
		}
		mArea.setViewAdapter(new ArrayWheelAdapter<String>(this, areas));
		mArea.setCurrentItem(0);
	}

	/**
	 * 根据当前的省,更新市WheelView的信息
	 */
	private void updateCities()
	{
		int pCurrent = mProvince.getCurrentItem();
		mCurrentProviceName = mProvinceDatas[pCurrent];
		String[] cities = mCitisDatasMap.get(mCurrentProviceName);
		if (cities == null)
		{
			cities = new String[] { "" };
		}
		mCity.setViewAdapter(new ArrayWheelAdapter<String>(this, cities));
		mCity.setCurrentItem(0);
		updateAreas();
	}

	/**
	 * 解析整个Json对象,完成后释放Json对象的内存
	 */
	private void initDatas()
	{
		try
		{
			JSONArray jsonArray = mJsonObj.getJSONArray("citylist");
			mProvinceDatas = new String[jsonArray.length()];
			for (int i = 0; i < jsonArray.length(); i++)
			{
				JSONObject jsonP = jsonArray.getJSONObject(i);// 每个省的json对象
				String province = jsonP.getString("p");// 省名字

				mProvinceDatas[i] = province;

				JSONArray jsonCs = null;
				try
				{
					/**
					 * Throws JSONException if the mapping doesn't exist or is
					 * not a JSONArray.
					 */
					jsonCs = jsonP.getJSONArray("c");
				} catch (Exception e1)
				{
					continue;
				}
				String[] mCitiesDatas = new String[jsonCs.length()];
				for (int j = 0; j < jsonCs.length(); j++)
				{
					JSONObject jsonCity = jsonCs.getJSONObject(j);
					String city = jsonCity.getString("n");// 市名字
					mCitiesDatas[j] = city;
					JSONArray jsonAreas = null;
					try
					{
						/**
						 * Throws JSONException if the mapping doesn't exist or
						 * is not a JSONArray.
						 */
						jsonAreas = jsonCity.getJSONArray("a");
					} catch (Exception e)
					{
						continue;
					}

					String[] mAreasDatas = new String[jsonAreas.length()];// 当前市的所有区
					for (int k = 0; k < jsonAreas.length(); k++)
					{
						String area = jsonAreas.getJSONObject(k).getString("s");// 区域的名称
						mAreasDatas[k] = area;
					}
					mAreaDatasMap.put(city, mAreasDatas);
				}

				mCitisDatasMap.put(province, mCitiesDatas);
			}

		} catch (JSONException e)
		{
			e.printStackTrace();
		}
		mJsonObj = null;
	}

	/**
	 * 从assert文件夹中读取省市区的json文件,然后转化为json对象
	 */
	private void initJsonData()
	{
		try
		{
			StringBuffer sb = new StringBuffer();
			InputStream is = getAssets().open("city.json");
			int len = -1;
			byte[] buf = new byte[1024];
			while ((len = is.read(buf)) != -1)
			{
				sb.append(new String(buf, 0, len, "gbk"));
			}
			is.close();
			mJsonObj = new JSONObject(sb.toString());
		} catch (IOException e)
		{
			e.printStackTrace();
		} catch (JSONException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * change事件的处理
	 */
	@Override
	public void onChanged(WheelView wheel, int oldValue, int newValue)
	{
		if (wheel == mProvince)
		{
			updateCities();
		} else if (wheel == mCity)
		{
			updateAreas();
		} else if (wheel == mArea)
		{
			mCurrentAreaName = mAreaDatasMap.get(mCurrentCityName)[newValue];
		}
	}

	public void showChoose(View view)
	{
		Toast.makeText(this, mCurrentProviceName + mCurrentCityName + mCurrentAreaName, 1).show();
	}
}

这样就完成了代码的编写,如果这篇文章对你有帮助,可以顶一个~嘿嘿~

源码下载点击这里

Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架

转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/23513993

本来准备下载个CSDN的客户端放手机上,没事可以浏览浏览资讯,下载了官方的之后,发现并不能很好的使用。恰好搜到一个大神自己写的csdn的app,下载安装了一下,感觉很不错,也很流畅,基本满足了我们 日常浏览的需求。

app效果图:




我会在博客中完整的介绍这个项目的制作,第一篇当然是整个项目的整体结构了。

效果图:



1、头部的布局文件,这个很简单:

<?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="wrap_content"
    android:background="@color/light_blue"
    android:orientation="horizontal" >


    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="4dp"
        android:src="@drawable/biz_navigation_tab_news_pressed" />


    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:src="@drawable/base_action_bar_back_divider" />


    <TextView
        android:id="@+id/headTV"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="4dp"
        android:layout_weight="1"
        android:text="CSDN资讯"
        android:textColor="@color/white"
        android:textSize="21sp"
        android:textStyle="bold" >
    </TextView>




</LinearLayout>

就显示一个图标和标题。

2、主布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eee"
    android:orientation="vertical" >


    <include layout="@layout/main_head" />


    <com.viewpagerindicator.TabPageIndicator
        android:id="@+id/id_indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/transparentblue" >
    </com.viewpagerindicator.TabPageIndicator>


    <android.support.v4.view.ViewPager
        android:id="@+id/id_pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />


</LinearLayout>

一个TabPageIndicator和一个ViewPager。

3、主Activity

package com.zhy.csdndemo;


import com.viewpagerindicator.TabPageIndicator;


import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;


public class MainActivity extends FragmentActivity
{
	private TabPageIndicator mIndicator ;
	private ViewPager mViewPager ;
	private FragmentPagerAdapter mAdapter ;


	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	
		mIndicator = (TabPageIndicator) findViewById(R.id.id_indicator);
		mViewPager = (ViewPager) findViewById(R.id.id_pager);
		mAdapter = new TabAdapter(getSupportFragmentManager());
		mViewPager.setAdapter(mAdapter);
		mIndicator.setViewPager(mViewPager, 0);
		
		
		
	}
	
	


}

TabAdapter.java

package com.zhy.csdndemo;


import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;


public class TabAdapter extends FragmentPagerAdapter
{


	public static final String[] TITLES = new String[] { "业界", "移动", "研发", "程序员杂志", "云计算" };


	public TabAdapter(FragmentManager fm)
	{
		super(fm);
	}


	@Override
	public Fragment getItem(int arg0)
	{
		MainFragment fragment = new MainFragment(arg0);
		return fragment;
	}


	@Override
	public CharSequence getPageTitle(int position)
	{
		return TITLES[position % TITLES.length];
	}


	@Override
	public int getCount()
	{
		return TITLES.length;
	}


}


MainFragment.java

package com.zhy.csdndemo;


import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;


@SuppressLint("ValidFragment")
public class MainFragment extends Fragment
{


	private int newsType = 0;


	public MainFragment(int newsType)
	{
		this.newsType = newsType;
	}


	@Override
	public void onActivityCreated(Bundle savedInstanceState)
	{
		super.onActivityCreated(savedInstanceState);
	}


	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
	{
		View view = inflater.inflate(R.layout.tab_item_fragment_main, null);
		TextView tip = (TextView) view.findViewById(R.id.id_tip);
		tip.setText(TabAdapter.TITLES[newsType]);
		return view;
	}


}

4、在styles.xml中自定义Theme

 

<style name="MyTheme" parent="AppBaseTheme">
        <item name="vpiTabPageIndicatorStyle">@style/MyWidget.TabPageIndicator</item>
        <item name="android:windowBackground">@drawable/init_pic</item>
        <item name="android:windowNoTitle">true</item>  
        <item name="android:animationDuration">5000</item>
        <item name="android:windowContentOverlay">@null</item>  
    </style>
    
    <style name="MyWidget.TabPageIndicator" parent="Widget">
        <item name="android:gravity">center</item>
        <item name="android:background">@drawable/vpi__tab_indicator</item>
        <item name="android:paddingLeft">22dip</item>
        <item name="android:paddingRight">22dip</item>
        <item name="android:paddingTop">8dp</item>
        <item name="android:paddingBottom">8dp</item>
        <item name="android:textAppearance">@style/MyTextAppearance.TabPageIndicator</item>
        <item name="android:textSize">16sp</item>
        <item name="android:maxLines">1</item>
    </style>


    <style name="MyTextAppearance.TabPageIndicator" parent="Widget">
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">@color/black</item>
    </style>

在AndroidManifest中注册使用:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zhy.csdndemo"
    android:versionCode="1"
    android:versionName="1.0" >


    <uses-sdk
        android:minSdkVersion="13"
        android:targetSdkVersion="17" />


    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.zhy.csdndemo.MainActivity"
            android:label="@string/app_name" 
            android:theme="@style/MyTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>


</manifest>

总体还是很简单的,但是效果很不错啊,现在不是流行Fragment么~好了 ,如果这篇文章对你有帮助,赞一个~

源码点击此处下载