SpannableString与SpannableStringBuilder使用
1、SpannableString、SpannableStringBuilder与String的关系
首先SpannableString、SpannableStringBuilder基本上与String差不多,也是用来存储字符串,但它们俩的特殊就在于有一个SetSpan()函数,能给这些存储的String添加各种格式或者称样式(Span),将原来的String以不同的样式显示出来,比如在原来String上加下划线、加背景色、改变字体颜色、用图片把指定的文字给替换掉,等等。所以,总而言之,SpannableString、SpannableStringBuilder与String一样, 首先也是传字符串,但SpannableString、SpannableStringBuilder可以对这些字符串添加额外的样式信息,但String则不行。
注意:如果这些额外信息能被所用的方式支持,比如将SpannableString传给TextView;也有对这些额外信息不支持的,比如前一章讲到的Canvas绘制文字,对于不支持的情况,SpannableString和SpannableStringBuilder就是退化为String类型,直接显示原来的String字符串,而不会再显示这些附加的额外信息。
2、SpannableString与SpannableStringBuilder区别
它们的区别在于 SpannableString像一个String一样,构造对象的时候传入一个String,之后再无法更改String的内容,也无法拼接多个 SpannableString;而SpannableStringBuilder则更像是StringBuilder,它可以通过其append()方法来拼接多个String;
3、SetSpan()
void setSpan (Object what, int start, int end, int flags)
函数意义:给SpannableString或SpannableStringBuilder特定范围的字符串设定Span样式,可以设置多个(比如同时加上下划线和删除线等),Falg参数标识了当在所标记范围前和标记范围后紧贴着插入新字符时的动作,即是否对新插入的字符应用同样的样式。
参数说明:
object what :对应的各种Span,后面会提到;
int start:开始应用指定Span的位置,索引从0开始
int end:结束应用指定Span的位置,特效并不包括这个位置。比如如果这里数为3(即第4个字符),第4个字符不会有任何特效。从下面的例子也可以看出来。
int flags:取值有如下四个
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE:前后都不包括,即在指定范围的前面和后面插入新字符都不会应用新样式
Spannable.SPAN_EXCLUSIVE_INCLUSIVE :前面不包括,后面包括。即仅在范围字符的后面插入新字符时会应用新样式
Spannable.SPAN_INCLUSIVE_EXCLUSIVE :前面包括,后面不包括。
Spannable.SPAN_INCLUSIVE_INCLUSIVE :前后都包括。
下面写了个小demo,先看一下效果图:
代码如下所示:
代码如下 | 复制代码 |
public class MainActivity extends Activity implements OnClickListener { |
在平时我们做项目中,或许有要对一张图片或者某一个东西进行文字和图片说明,这时候要求排版美观,所以会出现文字和图片混排的情况,如图:
这种情况就是上下两个文字说明是连续在一起的,这就要求我们计算上面的文字说明怎么和下面的文字说明连贯结合在一起呢,这就要求我们进行计算了,下面给出代码,代码中也有详细的注释,原理也很简单。
因为算是比较简单,直接就在activity中去计算了:
package com.example.test;
import android.app.Activity;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends Activity {
boolean imageMeasured = false;
TextView tv_right;
TextView tv_bottom;
static final String text = "叶凡:小说主角,与众老同学在泰山聚会时一同被九龙拉棺带离地球," +
"进入北斗星域,得知自己是荒古圣叶凡 叶凡体。历险禁地,习得源术,斗圣地世家,战太古生物," +
"重组天庭,叶凡辗转四方得到许多际遇和挑战,功力激增,眼界也渐渐开阔。一个浩大的仙侠世界," +
"就以他的视角在读者面前展开。姬紫月:姬家小姐,出场年龄十七岁。被叶凡劫持一同经历青铜古殿历险," +
"依靠碎裂的神光遁符解除禁制,反过来挟持叶凡一同进入太玄派寻找秘术。" +
"在叶凡逃离太玄后姬紫月在孔雀王之乱中被华云飞追杀,又与叶凡[2]相遇,被叶凡护送回姬家" +
",渐渐对叶凡产生微妙感情。后成为叶凡的妻子,千载后于飞仙星成仙,在叶凡也进入仙路后再见庞博:" +
"叶凡大学时最好的朋友,壮硕魁伟,直率义气。到达北斗星域后因服用了圣果被灵墟洞天作为仙苗," +
"在青帝坟墓处为青帝十九代孙附体离去,肉身被锤炼至四极境界。后叶凡与黑皇镇压老妖神识," +
"庞博重新掌控自己身躯,取得妖帝古经和老妖本体祭炼成的青莲法宝,习得妖帝九斩和天妖八式," +
"但仍伪装成老妖留在妖族。出关后找上叶凡,多次与他共进退。星空古路开启后由此离开北斗," +
"被叶凡从妖皇墓中救出,得叶凡授予者字秘、一气化三清,与叶凡同闯试炼古路,一起建设天庭";
// 屏幕的高度
int screenWidth = 0;
// 总共可以放多少个字
int count = 0;
// textView全部字符的宽度
float textTotalWidth = 0.0f;
// textView一个字的宽度
float textWidth = 0.0f;
Paint paint = new Paint();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_right = (TextView) findViewById(R.id.test_tv_right);
tv_bottom = (TextView) findViewById(R.id.test_tv_bottom);
final ImageView imageView = (ImageView) findViewById(R.id.test_image);
imageView.setImageResource(R.drawable.ee);
screenWidth = getWindowManager().getDefaultDisplay().getWidth();
/**
* 获取一个字的宽度
*/
textWidth = tv_right.getTextSize();
paint.setTextSize(textWidth);
/**
* 因为图片一开始的时候,高度是测量不出来的,通过增加一个监听器,即可获取其图片的高度和长度
*/
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
if (!imageMeasured) {
imageMeasured = true;
int height = imageView.getMeasuredHeight();
int width = imageView.getMeasuredWidth();
drawImageViewDone(width, height);
}
return imageMeasured;
}
});
}
private void drawImageViewDone(int width, int height) {
// 一行字体的高度
int lineHeight = tv_right.getLineHeight();
// 可以放多少行
int lineCount = (int) Math.ceil((double) height / (double) lineHeight);
// 一行的宽度
float rowWidth = screenWidth - width - tv_right.getPaddingLeft() - tv_right.getPaddingRight();
// 一行可以放多少个字
int columnCount = (int) (rowWidth / textWidth);
// 总共字体数等于 行数*每行个数
count = lineCount * columnCount;
// 一个TextView中所有字符串的宽度和(字体数*每个字的宽度)
textTotalWidth = (float) ((float) count * textWidth);
measureText();
tv_right.setText(text.substring(0, count));
// 检查行数是否大于设定的行数,如果大于的话,就每次减少一个字符,重新计算行数与设定的一致
while (tv_right.getLineCount() > lineCount) {
count -= 1;
tv_right.setText(text.substring(0, count));
}
tv_bottom.setPadding(0, lineCount * lineHeight - height, 0, 0);
tv_bottom.setText(text.substring(count));
}
/**
* 测量已经填充的长度,计算其剩下的长度
*/
private void measureText() {
String string = text.substring(0, count);
float size = paint.measureText(string);
int remainCount = (int) ((textTotalWidth - size) / textWidth);
if (remainCount > 0) {
count += remainCount;
measureText();
}
}
}
其中xml文件布局如下:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/test_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitXY" />
<TextView
android:id="@+id/test_tv_right"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/test_image"
android:gravity="fill_horizontal"
android:paddingLeft="7dp"
android:textSize="16sp" />
<TextView
android:id="@+id/test_tv_bottom"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/test_image"
android:gravity="fill_horizontal"
android:textSize="16sp" />
</RelativeLayout>
</ScrollView>
代码很少,原理也很简单,后来发现这种做法在大部分手机运行是完美的,但是少部分手机还是有点问题。是什么问题呢,是在我们测量textView的长度的是,因为是我们刚刚进行setText,然后马上进行测量,这样得到的结果是不正确的,所以大家可以优化一下。温馨提示,当我们setText之后,可以延时一些时间再去测量,这样获取的值就是挣钱的了,当然那个延迟的时间很短50毫秒就可以了,因为我们要相信textView的绘制速度还是很快的。
Android 文字环绕 图文混排 支持Span折叠
先直接上效果图
上图为实现目标,实现了Android图文混排,文字环绕,支持Span的识别,表情的嵌入,支持文字字体大小的设置等。
由于项目中需要用到图文混排技术,在此稍微研究了两天,出来一个效果还算不错的东西
图文混排技术,在不少Android应用中都已经实现,说穿了其实就是两个TextView加一个ImageView的布局罢了,代码里面实现下String的剪切就可以了,不过我这里的这个除了要实现混排效果外,还要支持Span,支持表情等,这就有点麻烦了。下面慢慢分解。先贴出RichTextImageView的布局。
<?xml version="1.0" encoding="utf-8"?>
<!-- com.demonzym.richtextdemo.RichTextImageView -->
<com.demonzym.richtextdemo.RichTextImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:id="@+id/richview" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<RelativeLayout
android:id="@+id/layout_preimage_isgif_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:visibility="gone" >
<ImageView
android:id="@+id/preimage_statues_left"
android:layout_width="134dip"
android:layout_height="134dip"
android:background="@drawable/preview_back"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:src="@drawable/preview_back_small" />
<ImageView
android:id="@+id/preimage_isgif_left"
android:layout_width="24dip"
android:layout_height="17dip"
android:layout_alignBottom="@+id/preimage_statues"
android:layout_alignRight="@+id/preimage_statues"
android:layout_marginBottom="9dip"
android:layout_marginRight="7dip" />
</RelativeLayout>
<com.demonzym.richtextdemo.RichTextView
android:id="@+id/lefttext"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"/>
<RelativeLayout
android:id="@+id/layout_preimage_isgif_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:visibility="gone" >
<ImageView
android:id="@+id/preimage_statues_right"
android:layout_width="134dip"
android:layout_height="134dip"
android:background="@drawable/preview_back"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:src="@drawable/preview_back_small" />
<ImageView
android:id="@+id/preimage_isgif_right"
android:layout_width="24dip"
android:layout_height="17dip"
android:layout_alignBottom="@+id/preimage_statues"
android:layout_alignRight="@+id/preimage_statues"
android:layout_marginBottom="9dip"
android:layout_marginRight="7dip" />
</RelativeLayout>
</LinearLayout>
<com.demonzym.richtextdemo.RichTextView
android:id="@+id/bottomtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</com.demonzym.richtextdemo.RichTextImageView>
布局中的RichTextView是另外封装的一个实现Span的TextView,就是实现效果图中表情啊,@啊,#话题# 之类的,了解Span的都懂的,就不细说了。读者研究这个图文混排的时候,可以直接用TextView替代。
RichTextImageView 即本文中的图文混排的View。继承于LinearLayout,该类实现的关键代码如下:
public void setText(String t) {
mTopText.setText("");
mBottomText.setText("");
mTopText.setVisibility(View.INVISIBLE);
mBottomText.setVisibility(View.GONE);
bRequestBottomLayout = true;
if (t == null)
t = "";
// Log.e("setText", t);
mTextContent = t;
mTopText.setText(mTextContent);
requestLayout();
}
public void setImage(int id) {
mImageContent = id;
mPreviewImage.setImageResource(id);
mPreview.setVisibility(View.VISIBLE);
if (!Util.isStringEmpty(mTextContent))
setText(mTextContent);
}
private void iniViews() {
mLinearLayout = (LinearLayout) findViewById(R.id.linearLayout1);
mTopText = (RichTextView) findViewById(R.id.lefttext);
mBottomText = (RichTextView) findViewById(R.id.bottomtext);
if(IMAGE_LOCATION == IMAGE_LOCATION_RIGHT){
mPreview = (RelativeLayout) findViewById(R.id.layout_preimage_isgif_right);
mPreviewImage = (ImageView) findViewById(R.id.preimage_statues_right);
mPreviewImageIsGif = (ImageView) findViewById(R.id.preimage_isgif_right);
}else if(IMAGE_LOCATION == IMAGE_LOCATION_LEFT){
mPreview = (RelativeLayout) findViewById(R.id.layout_preimage_isgif_left);
mPreviewImage = (ImageView) findViewById(R.id.preimage_statues_left);
mPreviewImageIsGif = (ImageView) findViewById(R.id.preimage_isgif_left);
}
}
public void setImageLocation(int l){
if(l != IMAGE_LOCATION_RIGHT && l != IMAGE_LOCATION_LEFT)
return;
else{
IMAGE_LOCATION = l;
mPreview.setVisibility(View.GONE);
iniViews();
setImage(mImageContent);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
if (Util.isStringEmpty(mTextContent))
return;
// Log.e("top text 宽 高",
// mTopText.getWidth() + " " + mTopText.getHeight());
// Log.e("top text 单行高度", "" + mTopText.getLineHeight());
// Rect rect = getStringRect(mTextContent, mTopText.getPaint());
// Log.e("文本的宽 高", rect.width() + " " + rect.height());
// toptext最多能显示的行数
int topTextMaxRows = mTopText.getHeight() / mTopText.getLineHeight();
// 显示这些内容真实占用的行数
int textRows = mTopText.getLineCount();
// Log.e("top text最多能显示的行数", "" + topTextMaxRows);
// Log.e("当前文本实际占用的行数", "" + textRows);
//由于文本可能带有表情,重新计算显示行数,以保证显示正确
Rect lineR = new Rect();
int realH = 0; //toptext真实高度
int i = 0;
for(i = 0; i < topTextMaxRows; i++){
try{
mTopText.getLineBounds(i, lineR);
}catch(IndexOutOfBoundsException e){
break;
}
realH += lineR.height();
if(realH >= mTopText.getHeight())
break;
}
// Log.e("当前view实际能显示的行数为", "" + i);
topTextMaxRows = i;
//如果toptext显示不下的话,显示到bottomtext里面去
if (textRows >= topTextMaxRows && bRequestBottomLayout) {
// toptext最后一个可见字符的位置
int lastindex = mTopText.getLayout().getLineVisibleEnd(
topTextMaxRows - 1);
ClickableSpan[] cs = mTopText.getSpans();
int spanstart = 0;
int spanend = 0;
spanstring = "";
STATE = 0; // 1网页 2人名 3话题
for (ClickableSpan c : cs) {
spanstart = mTopText.getSpanStart(c);
spanend = mTopText.getSpanEnd(c);
if (spanstart <= lastindex && spanend > lastindex) {
if (c instanceof LinkClickableSpan) {
// Log.e("转角span类型", "网页");
spanstring = ((LinkClickableSpan) c).getLink();
STATE = 1;
}
if (c instanceof NameClickableSpan) {
// Log.e("转角span类型", "人名");
spanstring = ((NameClickableSpan) c).getName();
STATE = 2;
}
if (c instanceof TopicClickableSpan) {
// Log.e("转角span类型", "话题");
spanstring = ((TopicClickableSpan) c).getTopic();
STATE = 3;
}
break;
}
}
mTopText.setText(mTextContent.substring(0, lastindex));
mTopText.setVisibility(View.VISIBLE);
// 当行数不是整数时,调整内容会有下面半行没遮住,所以干脆都处理完以后再显示出来
mBottomText.setText(mTextContent.substring(lastindex,
mTextContent.length()) + mBottomText.getText().toString());
if (mBottomText.getText().length() > 0 && bRequestBottomLayout){
mBottomText.setVisibility(View.VISIBLE);
mHandler.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mBottomText.requestLayout();
bRequestBottomLayout = false;
}
});
}
if (STATE != 0 || !bRequestBottomLayout) {
Log.e("spanstring", spanstring);
// Log.e("", "移除转角span");
mTopText.getSpannable().removeSpan(mTopText.getSpans()[mTopText.getSpans().length - 1]);
switch (STATE) {
case 1:
mTopText.getSpannable().setSpan(
mTopText.new LinkClickableSpan(spanstring),
spanstart,
lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mBottomText.getSpannable().setSpan(
mBottomText.new LinkClickableSpan(spanstring),
0,
spanend - lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Log.e("", "网页");
break;
case 2:
mTopText.getSpannable().setSpan(
mTopText.new NameClickableSpan(spanstring),
spanstart,
lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mBottomText.getSpannable().setSpan(
mBottomText.new NameClickableSpan(spanstring),
0,
spanend - lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Log.e("", "人名");
break;
case 3:
mTopText.getSpannable().setSpan(
mTopText.new TopicClickableSpan(spanstring),
spanstart,
lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mBottomText.getSpannable().setSpan(
mBottomText.new TopicClickableSpan(spanstring),
0,
spanend - lastindex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Log.e("", "话题");
break;
}
mTopText.setText(mTopText.getSpannable(), BufferType.SPANNABLE);
mBottomText.setText(mBottomText.getSpannable(), BufferType.SPANNABLE);
}
}
}
说明下难点:
第一,转角处可能存在Span,比如一个话题 #话题话题话题话题话题话题话题话题话题#,可能一半内容在mTopText里面的最后一行显示,而另一半显示不下了,这就需要剪切剩余的String显示到mBottomText里面去,可是Span的内容就会出错,这就需要重新设置下转角处的Span效果了
第二,由于存在图片Span,会导致行高不正确,所以需要在获取到mTopText能显示的最多行数以后,重新判断一次是否正确,如果不正确的话,需要重新调整,不然mTopText会有一部分内容显示不下。
该View实现完成后,在布局中使用include条用,代码中find出来就可以直接使用了
不考虑Span的话,直接将代码中转角处理Span部分删除即可使用,我注释的挺清楚了
Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件,这些都可以让你胡乱修改的意思)。 Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。
我们在电脑上进行网络访问请求调试的时候通常在浏览器中可以使用F12,但是如果要看手机访问时进行了哪些请求那就不那么简单了,而使用 Fiddler 则可以很方便的看到数据请求与响应。
下面介绍 Fiddler 的安装与使用。
前提:电脑和手机保持在同一局域网内
1. 安装Fiddler
安装程序:
2. 安装完成后启动软件,依次点击 Tools --> Fiddler Options... 如下图:
3. 点击 Connections , 将 Allow remote computers connect 勾选上,监听端口为8888,如图:
4.依次点击关闭后,重启软件。
5. 查看电脑的IP,我这里是 192.168.1.4
6. 在手机上,将手机代理设置为电脑上设置 192.168.1.4 和 8888 如下图:
7. 如下图,已经开始工作了:
AndBase框架为我们提供了一些相关的方法提供给我们使用,用来完成Http网络请求...总体就是对Http请求的一个封装,不过个人认为,网络请求这一模块更加推荐使用Volley框架..楼主对比了两个框架中的源码...Volley更多的地方是使用抽象方法封装在接口内,然后对外暴露接口,其他类在实现接口的同时需要实现内部的抽象方法...而AndBase则是使用继承的方式..继承父类..实现类通过重写的方式对封装的方法重写从而进行下一步的操作...
相比二者网络请求的源码,Volley源码的书写还是更胜一筹...Volley是Google推出的,针对的也仅仅是网络请求这一模块...同样AndBase也是非常优秀的,是国内牛人写的一款重量级框架,涉及的模块非常的广泛,还是非常好用的...
1.使用AndBase框架实现无参Http Get请求...
一般普通的网络请求如果不涉及到数据信息的变化,也就是不涉及一些安全性问题,都可以使用Get方式来完成网络请求...Get请求也是分为有参和无参的,给我的感觉有参一般可以用于向服务器上传资源数据...先介绍一下无参的Get请求...还是先从源码的地方来看看...
源码的调用方式是先使用AbHttpUtils.get()函数调用...不过这无关紧要...通过这个方法走向AbHttpClient类内部...执行下面这段源码...无论是有参还是无参..都会调用这个方法..无参的时候第二个参数传递null就行了...
public void get(final String url,final AbRequestParams params,final AbHttpResponseListener responseListener) {
responseListener.setHandler(new ResponderHandler(responseListener));
executorService.submit(new Runnable() {
public void run() {
try {
doGet(url,params,responseListener);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
我们可以看到,这段函数首先通过Handler发送Message...同时开启一个线程池,来提交当前的请求...最后将执行doGet()方法...同时Handler一直对responseListener的消息进行处理..doGet()方法的源码过程如下...
private void doGet(String url,AbRequestParams params,AbHttpResponseListener responseListener){
try {
responseListener.sendStartMessage();
if(!debug && !AbAppUtil.isNetworkAvailable(mContext)){
responseListener.sendFailureMessage(AbConstant.CONNECT_FAILURE_CODE,AbConstant.CONNECTEXCEPTION, new AbAppException(AbConstant.CONNECTEXCEPTION));
return;
}
//HttpGet连接对象
if(params!=null){
url += params.getParamString(); //如果有参,那么获取相关参数...
}
HttpGet httpRequest = new HttpGet(url); //定义连接对象..
BasicHttpParams httpParams = new BasicHttpParams();
// 从连接池中取连接的超时时间,设置为1秒
ConnManagerParams.setTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(DEFAULT_MAX_CONNECTIONS));
ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS);
// 读响应数据的超时时间
HttpConnectionParams.setSoTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
HttpConnectionParams.setConnectionTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
HttpConnectionParams.setTcpNoDelay(httpParams, true);
HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE);
//设置协议版本...
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setUserAgent(httpParams, String.format("andbase-http/%s (http://www.418log.org/)", 1.0));
// 设置请求参数
httpRequest.setParams(httpParams);
//取得HttpClient对象
HttpClient httpClient = new DefaultHttpClient();
//请求HttpClient,取得HttpResponse
HttpResponse httpResponse = httpClient.execute(httpRequest);
//请求成功
int statusCode = httpResponse.getStatusLine().getStatusCode();
//取得返回的字符串
HttpEntity mHttpEntity = httpResponse.getEntity();
if (statusCode == HttpStatus.SC_OK){
if(responseListener instanceof AbStringHttpResponseListener){
String content = EntityUtils.toString(mHttpEntity);
((AbStringHttpResponseListener)responseListener).sendSuccessMessage(statusCode, content);
}else if(responseListener instanceof AbBinaryHttpResponseListener){
readResponseData(mHttpEntity,((AbBinaryHttpResponseListener)responseListener));
}else if(responseListener instanceof AbFileHttpResponseListener){
//获取文件名
String fileName = AbFileUtil.getFileNameFromUrl(url, httpResponse);
writeResponseData(mHttpEntity,fileName,((AbFileHttpResponseListener)responseListener));
}
}else{
String content = EntityUtils.toString(mHttpEntity);
responseListener.sendFailureMessage(statusCode, content, new AbAppException(AbConstant.UNKNOWNHOSTEXCEPTION));
}
} catch (Exception e) {
e.printStackTrace();
//发送失败消息
responseListener.sendFailureMessage(AbConstant.UNTREATED_CODE,e.getMessage(),new AbAppException(e));
}finally{
responseListener.sendFinishMessage();
}
}
有了上面的源码调用过程其实就非常的清晰了..
无论是doGet()方法还是doPost()方法模式基本是相同的,都是需要先建立一个连接对象,HttpGet或HttpPost..不同之处在于有参的Get请求直接将params加入到url后面即可,而Post请求需要获取实体数据..在实体数据中加入我们传递的params..设置连接过程和读取数据过程中的相关参数,比如说超时的时间,使用的Http版本,设置UserAgent等等...设置完之后执行请求获取响应了...
中间涉及到了一个判断的过程..判断返回的响应数据到底属于什么类型的数据,是基本的String类型,还是与图片或者视频相关的Byte类型,还是与文件相关的File类型...通过对相关类型的判断,执行不同的方法,虽然方法不相同,但是最后的目的是一样的,都是把实体数据进行封装...封装完毕后调用sendSuccessMessage然后Handler自动回去处理Message...最后调用OnSuccess方法..将数据返回给客户端..
还是看一下实际的调用过程...
无参的Get请求调度...这里需要设置相应监听...
public void FileClick(View v){
url="http://192.168.199.172:8080/JSP/imageview.jpg";
getView();
httpUtil.get(url, new FileResponseListener(this, this, v,max_tv,num_tv,progressBar));
}
GetResponseListener.java
对响应的监听的一个重写过程...通过为请求设置上url+相关监听就能够完成网络请求,并对请求数据进行相关处理了...这里完成了一个图片数据的下载,然后通过对数据进行封装,就成了一个Bitmap..这样就能够在控件上进行显示了..
package com.example.andbasehttp;
import java.io.File;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.ab.activity.AbActivity;
import com.ab.http.AbFileHttpResponseListener;
import com.ab.util.AbFileUtil;
import com.ab.view.progress.AbHorizontalProgressBar;
public class FileResponseListener extends AbFileHttpResponseListener{
private int max=100;
private int progress=0;
private AbActivity activity;
private Context context;
private AlertDialog dialog;
private View view;
private TextView max_tv,num_tv;
private AbHorizontalProgressBar progressBar;
public FileResponseListener(AbActivity activity,Context context,View v,TextView v1,TextView v2, AbHorizontalProgressBar progressBar ){
this.activity=activity;
this.context=context;
this.view=v;
this.max_tv=v1;
this.num_tv=v2;
this.progressBar=progressBar;
}
@Override
public void onSuccess(int statusCode, File file){
Bitmap bitmap=AbFileUtil.getBitmapFromSD(file);
ImageView view=new ImageView(context);
view.setImageBitmap(bitmap);
activity.showDialog("返回结果", view, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
}
@Override
public void onFailure(int statusCode, String content,Throwable error){
activity.showToast(error.toString());
}
@Override
public void onStart(){
max_tv.setText(progress+"/"+String.valueOf(max));
progressBar.setMax(max);
progressBar.setProgress(progress);
dialog=activity.showDialog("正在下载", view);
}
@Override
public void onProgress(int bytesWritten, int totalSize){
max_tv.setText(bytesWritten/(totalSize/max)+"/"+max);
progressBar.setProgress(bytesWritten/(totalSize/max));
}
@Override
public void onFinish(){
dialog.cancel();
dialog=null;
}
}
2.使用AndBase框架实现有参Http Post请求...
其实调用的方式都是相同的,,只不过Post请求需要传递相关的参数...使用有参的Post请求...这里是向一个JSP传递相关参数来完成数据信息的验证...
public void PostClick(View v){
url="http://192.168.199.172:8080/JSP/post.jsp";
params=new AbRequestParams();
params.put("name", "darker");
params.put("password", "49681888");
httpUtil.post(url, params, new PostResponseListener(this));
}
这里我就不粘贴PostResponseListener的代码了...贴一下JSP页面的代码..相关的JSP代码如下...这里的JSP代码非常的简单..并且前面在使用Volley的时候也使用过..JSP页面我们完全可以自己书写的更加复杂一些,那么就能够实现更多的功能...
<%
String name=request.getParameter("name");
String password=request.getParameter("password");
if("darker".equals(name)&& "49681888".equals(password)){
out.println("Receive name is:"+name);
out.println("Receive password is:"+password);%>
Your Message are right!
<%}else{
out.println("Receive name is:"+name);
out.println("Receive password is:"+password);%>
Your Message are wrong!
<%}%>
3.使用AndBase框架实现有参Http Get请求...
有参的Get请求一般用于文件,数据资源的上传...将上传的资源以及名称作为参数传递给服务器..这里不涉及安全上的问题..因此可以使用带有参数的Get请求...这里向服务器上传文件..需要添加相关参数...
public void FileLoadClick(View v){
url="http://192.168.199.172:8080";
AbRequestParams params = new AbRequestParams();
File pathRoot = Environment.getExternalStorageDirectory();
String path = pathRoot.getAbsolutePath();
File file1 = new File(path+"/download/cache_files/aa.txt");
params.put(file1.getName(),file1);
getView();
httpUtil.get(url, params, new FileSendResponseListener(this, this, v, max_tv, num_tv, progressBar));
}
这里的监听事件简单的粘贴一下...监听事件之所以传递控件..是为了更好的向用户进行展示...这里设置了一个进度条的方式,来贯穿整个请求——响应的过程...如果下载或者是上传的文件和资源过多...我们是必须通知用户相关进度的..总不能一直卡死在界面上..这样用户也无法知道到底是否完成了数据的上传或者是下载...
package com.example.andbasehttp;
import android.app.AlertDialog;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import com.ab.activity.AbActivity;
import com.ab.http.AbStringHttpResponseListener;
import com.ab.view.progress.AbHorizontalProgressBar;
public class FileSendResponseListener extends AbStringHttpResponseListener{
private int max=100;
private int progress=0;
private AbActivity activity;
private Context context;
private AlertDialog dialog;
private View view;
private TextView max_tv,num_tv;
private AbHorizontalProgressBar progressBar;
public FileSendResponseListener(AbActivity activity,Context context,View v,TextView v1,TextView v2, AbHorizontalProgressBar progressBar ){
this.activity=activity;
this.context=context;
this.view=v;
this.max_tv=v1;
this.num_tv=v2;
this.progressBar=progressBar;
}
@Override
public void onSuccess(int statusCode, String content){
activity.showToast("OnSuccess");
System.out.println(content);
}
@Override
public void onFailure(int statusCode, String content,Throwable error){
activity.showToast(error.toString());
}
@Override
public void onStart(){
max_tv.setText(progress+"/"+String.valueOf(max));
progressBar.setMax(max);
progressBar.setProgress(progress);
activity.showToast("正在下载");
dialog=activity.showDialog("正在下载", view);
}
@Override
public void onProgress(int bytesWritten, int totalSize){
max_tv.setText(bytesWritten/(totalSize/max)+"/"+max);
progressBar.setProgress(bytesWritten/(totalSize/max));
}
@Override
public void onFinish(){
dialog.cancel();
dialog=null;
}
}
涉及到的类为com.ab.http保内的所有类...
1.AbStringHttpResponseListener.java
2.AbBinaryHttpResponseListener.java
3.AbFileHttpResponseListener.java
这三个类是对AbHttpResponseListener.java的一个继承...继承了其内部的一些相关方法..包括请求开始,结束,失败等等函数...
AbHttpClient.java就是用来完成请求——连接过程的实现...其中还包含数据的封装..
AbHttpUtils.java则是对post,get等方法调用的一个中间层...
AbRequestParams.java 则是对请求参数处理的一个类...不仅包含对请求参数的处理,还包含对实体的创建..为实体添加相关参数等方法的实现过程...
Android Http Get和Post请求
通过HttpGet和HttpPost向服务器提交请求,并从服务器返回结果信息。通过如下3步访问Http资源。
(1)创建HttpGet或者HttpPost对象,将要请求的URL通过构造方法传入HttpGet或HttpPost对象。
(2)使用DefaultHttpClient.execute方法发送Http Get或Http Post请求,并返回HttpResponse对象。
(3)通过HttpResponse.getEntity方法返回响应信息,并进行相应的处理。
如果使用HttpPost方法提交Http Post请求,还需要使用HttpPost.setEntity方法设置请求参数。
实例代码如下:
package mobile.android.ch13.httpgetpost;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class Main extends Activity implements OnClickListener
{
@Override
public void onClick(View view)
{
String url = "http://172.22.20.194:8080/querybooks/QueryServlet";
TextView tvQueryResult = (TextView) findViewById(R.id.tvQueryResult);
EditText etBookName = (EditText) findViewById(R.id.etBookName);
HttpResponse httpResponse = null;
try
{
switch (view.getId())
{
case R.id.btnGetQuery:
url += "?bookname=" + etBookName.getText().toString();
HttpGet httpGet = new HttpGet(url);
httpResponse = new DefaultHttpClient().execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200)
{
String result = EntityUtils.toString(httpResponse
.getEntity());
tvQueryResult.setText(result.replaceAll("\r", ""));
}
break;
case R.id.btnPostQuery:
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("bookname", etBookName
.getText().toString()));
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = new DefaultHttpClient().execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == 200)
{
String result = EntityUtils.toString(httpResponse
.getEntity());
tvQueryResult.setText(result.replaceAll("\r", ""));
}
break;
}
}
catch (Exception e)
{
tvQueryResult.setText(e.getMessage());
}
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnGetQuery = (Button) findViewById(R.id.btnGetQuery);
Button btnPostQuery = (Button) findViewById(R.id.btnPostQuery);
btnGetQuery.setOnClickListener(this);
btnPostQuery.setOnClickListener(this);
}
}
首先,在Android studio的一个工程里,在Gradle Scripts目录下,有一个文件build.gradle(Module:app),打开。这里我随便选了一个工程的build.gradle文件做例子:
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':xDroid')
compile project(':UniversalImageLoader')
compile project(':xdroidrequest')
compile project ( ':library')
}
compileSdkVersion 21,说明要运行该源码,你必选已经安装了android API 21。
buildToolsVersion 21.1.2 说明要运行该源码,你必须已经安装了 android sdk build-tools 21.1.2。
minSdkVerison 表示向下低至android API 14,即androd 4.0和4.0以上的版本都可以运行该工程。
targetSdkVerision 表示采用的目标android API是 API 21即 android 5.0。
下面的dependencies里指明的就是一些需要用到的第三方库。
相关文章
- 有时为了网站安全和版权问题,会对自己写的php源码进行加密,在php加密技术上最常用的是zend公司的zend guard 加密软件,现在我们来图文讲解一下。 下面就简单说说如何...2016-11-25
- 这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
- ps软件是现在很多人都会使用到的,HSL面板在ps软件中又有着非常独特的作用。这次文章就给大家介绍下ps怎么使用HSL面板,还不知道使用方法的下面一起来看看。  ...2017-07-06
mybatis 返回Integer,Double,String等类型的数据操作
这篇文章主要介绍了mybatis 返回Integer,Double,String等类型的数据操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-25- 许多的朋友对于Plesk控制面板应用不是非常的了解特别是英文版的Plesk控制面板,在这里小编整理了一些关于Plesk控制面板常用的使用方案整理,具体如下。 本文基于Linu...2016-10-10
使用insertAfter()方法在现有元素后添加一个新元素
复制代码 代码如下: //在现有元素后添加一个新元素 function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if (parent.lastChild == targetElement){ parent.appendChild(newEl...2014-05-31- 大概有如下步骤 新建项目Bejs 新建文件package.json 新建文件Gruntfile.js 命令行执行grunt任务 一、新建项目Bejs源码放在src下,该目录有两个js文件,selector.js和ajax.js。编译后代码放在dest,这个grunt会...2014-06-07
使用percona-toolkit操作MySQL的实用命令小结
1.pt-archiver 功能介绍: 将mysql数据库中表的记录归档到另外一个表或者文件 用法介绍: pt-archiver [OPTION...] --source DSN --where WHERE 这个工具只是归档旧的数据,不会对线上数据的OLTP查询造成太大影响,你可以将...2015-11-24如何使用php脚本给html中引用的js和css路径打上版本号
在搜索引擎中搜索关键字.htaccess 缓存,你可以搜索到很多关于设置网站文件缓存的教程,通过设置可以将css、js等不太经常更新的文件缓存在浏览器端,这样访客每次访问你的网站的时候,浏览器就可以从浏览器的缓存中获取css、...2015-11-24jQuery 1.9使用$.support替代$.browser的使用方法
jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support 。 在更新的 2.0 版本中,将不再支持 IE 6/7/8。 以后,如果用户需要支持 IE 6/7/8,只能使用 jQuery 1.9。 如果要全面支持 IE,并混合...2014-05-31安装和使用percona-toolkit来辅助操作MySQL的基本教程
一、percona-toolkit简介 percona-toolkit是一组高级命令行工具的集合,用来执行各种通过手工执行非常复杂和麻烦的mysql和系统任务,这些任务包括: 检查master和slave数据的一致性 有效地对记录进行归档 查找重复的索...2015-11-24- 一、下载 mysqlsla [root@localhost tmp]# wget http://hackmysql.com/scripts/mysqlsla-2.03.tar.gz--19:45:45-- http://hackmysql.com/scripts/mysqlsla-2.03.tar.gzResolving hackmysql.com... 64.13.232.157Conn...2015-11-24
- 目前,JSON已经成为最流行的数据交换格式之一,各大网站的API几乎都支持它。我写过一篇《数据类型和JSON格式》,探讨它的设计思想。今天,我想总结一下PHP语言对它的支持,这是开发互联网应用程序(特别是编写API)必须了解的知识...2015-10-30
- 无限级分类在开发中经常使用,例如:部门结构、文章分类。无限级分类的难点在于“输出”和“查询”,例如 将文章分类输出为<ul>列表形式; 查找分类A下面所有分类包含的文章。1.实现原理 几种常见的实现方法,各有利弊。其中...2015-10-23
- C#注释的一些使用方法浅谈,需要的朋友可以参考一下...2020-06-25
- php类的使用实例教程 <?php /** * Class program for yinghua05-2 * designer :songsong */ class Template { var $tpl_vars; var $tpl_path; var $_deb...2016-11-25
- 前几天在百度知道里面看到有人问PHP中双冒号::的用法,当时给他的回答比较简洁因为手机打字不大方便!今天突然想起来,所以在这里总结一下我遇到的双冒号::在PHP中使用的情况!双冒号操作符即作用域限定操作符Scope Resoluti...2015-11-08
- 本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
- mysqli封装了诸如事务等一些高级操作,同时封装了DB操作过程中的很多可用的方法。应用比较多的地方是 mysqli的事务。...2013-10-02
- 这篇文章主要介绍了Substring截取字符串方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25