- 浏览: 178622 次
- 性别:
- 来自: 云南
文章分类
最新评论
-
chencangui:
楼主能分享下源码吗?蟹蟹了
Android使用VideoView播放网络视频 -
liulongke:
表示看完完全没看出来是繁体字的默默走过。。。
PagerAdapter用法 -
Alexia23:
很赞很详细!
JAVA列出目录下所有的文件&文件夹 -
snso001:
繁体字恶心了。。。
PagerAdapter用法 -
wushanlin123:
楼主,你的繁体字让我蛋碎了一地
PagerAdapter用法
收藏列表
- 全部 [28]
- 默认 [2]
- string操作 [1]
- java数组 [7]
- java递归 [1]
- 安卓通话记录代码片段 [1]
- 背景选择器 [1]
- 手机短信基础 [1]
- java中for循环 [8]
- android实现数据存储技术 [1]
- android实现局部图片滑动指引效果 [1]
- 字母排序 [1]
- string 一个一个打印 [1]
- string 求和 [1]
- java中int、string的类型转换 [1]
标题 | 标签 | 来源 | |
反编译apk | http://blog.csdn.net/take_all/article/details/7163656 | ||
http://blog.csdn.net/take_all/article/details/7163656 |
|||
发布app到GoogLe Market教程 . | http://blog.csdn.net/woshicaixianfeng/article/details/6447237 | ||
http://blog.csdn.net/woshicaixianfeng/article/details/6447237 |
|||
JAVA中int、String的类型转换 | java中int、string的类型转换 | ||
int -> String int i=12345; String s=""; 第一种方法:s=i+""; 第二种方法:s=String.valueOf(i); 这两种方法有什么区别呢?作用是不是一样的呢?是不是在任何下都能互换呢? String -> int s="12345"; int i; 第一种方法:i=Integer.parseInt(s); 第二种方法:i=Integer.valueOf(s).intValue(); 这两种方法有什么区别呢?作用是不是一样的呢?是不是在任何下都能互换呢? 以下是答案: 第一种方法:s=i+""; //会产生两个String对象 第二种方法:s=String.valueOf(i); //直接使用String类的静态方法,只产生一个对象 第一种方法:i=Integer.parseInt(s);//直接使用静态方法,不会产生多余的对象,但会抛出异常 第二种方法:i=Integer.valueOf(s).intValue();//Integer.valueOf(s) 相当于 new Integer(Integer.parseInt(s)),也会抛异常,但会多产生一个对象 -------------------------------------------------------------------- 1如何将字串 String 转换成整数 int? A. 有两个方法: 1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([String],[int radix]); 2). int i = Integer.valueOf(my_str).intValue(); 注: 字串转成 Double, Float, Long 的方法大同小异. 2 如何将整数 int 转换成字串 String ? A. 有叁种方法: 1.) String s = String.valueOf(i); 2.) String s = Integer.toString(i); 3.) String s = "" + i; 注: Double, Float, Long 转成字串的方法大同小异. JAVA数据类型转换 ynniebo [收藏] 关键字 类型转换 出处 这是一个例子,说的是JAVA中数据数型的转换.供大家学习引 package cn.com.lwkj.erts.register; import java.sql.Date; public class TypeChange { public TypeChange() { } //change the string type to the int type public static int stringToInt(String intstr) { Integer integer; integer = Integer.valueOf(intstr); return integer.intValue(); } //change int type to the string type public static String intToString(int value) { Integer integer = new Integer(value); return integer.toString(); } //change the string type to the float type public static float stringToFloat(String floatstr) { Float floatee; floatee = Float.valueOf(floatstr); return floatee.floatValue(); } //change the float type to the string type public static String floatToString(float value) { Float floatee = new Float(value); return floatee.toString(); } //change the string type to the sqlDate type public static java.sql.Date stringToDate(String dateStr) { return java.sql.Date.valueOf(dateStr); } //change the sqlDate type to the string type public static String dateToString(java.sql.Date datee) { return datee.toString(); } public static void main(String[] args) { java.sql.Date day ; day = TypeChange.stringToDate("2003-11-3"); String strday = TypeChange.dateToString(day); System.out.println(strday); } } JAVA中常用数据类型转换函数 |
|||
求String里面对其中数字求和 | string 求和 | ||
package core.day01; public class LianXi2 { public static void main(String[] args) { String s = "124fdafda234ere23"; String t = ""; int sum = 0; for(int i = 0; i < s.length();i++){ char a = s.charAt(i); if(a>='0'&&a<='9'){ t = t + a; }else{ if(!"".equals(t)){ sum += Integer.parseInt(t); t = ""; } } } if(!"".equals(t)){ sum += Integer.parseInt(t); } System.out.println(sum); } } |
|||
打印如下字符串中的数字 一个一个打印 | string 一个一个打印 | ||
/*打印如下字符串中的数字 一个一个打印即可*/ String s13 = "aAGCZbc123defe234fd2"; for(int i = 0; i < s13.length();i++){ char a = s13.charAt(i); if(a>='0' && a<='9'){ System.out.println(a); } } System.out.println("=============="); /*打印字母呢*/ for(int i = 0; i < s13.length();i++){ char a= s13.charAt(i); if(a>='a'&&a<='z' || a>='A'&&a<='Z'){ System.out.print(a+","); } } System.out.println(); String s14 = "123"; System.out.println(s14+20); /*把一个字符串数组转换成整数*/ int a = Integer.parseInt(s14); System.out.println(a+20); int b = 123; String s15 = b+""; /*valueOf能够把其它类型转换成字符串*/ s15 = String.valueOf(b); System.out.println("=============="); |
|||
要求按字母顺序给数组元素排序 | 字母排序 | ||
* 练习1:String[] str = * {"xyz","abc","ade","bad","bac"} * 要求按字母顺序给数组元素排序 * 请不使用Arrays.sort * */ String str[] = {"xyz","abc","ade","bac"}; //Arrays.sort(str); // System.out.println(Arrays.toString(str)); for(int i = 0 ; i < str.length-1;i++){ for(int j = i + 1; j < str.length;j++){ if(str[i].compareTo(str[j])>0){ String t = str[i]; str[i] = str[j]; str[j] = t; } } } System.out.println(Arrays.toString(str)); /*字符串类重写了equals 两个字符串用equals比较的是内容是否相同*/ String s9 = "aa"; String s10 = "aa"; System.out.println(s9==s10); System.out.println(s9.equals(s10)); String s11 = "hello"; String s12 = new String("hello"); System.out.println(s11==s12); System.out.println(s11.equals(s12)); |
|||
String操作 | string操作 | ||
String s1 = "hello"; String s2 = "world"; /*concat底层用的是字符数组拷贝的操作Demo1的案例中*/ System.out.println(s1.concat(s2)); String s3 = "aa.bb.cc.dd.A.class"; /*取文件扩展名 找到最后一个点的位置*/ int last = s3.lastIndexOf("."); System.out.println(last); /*从last+1截取到最后*/ String s4 = s3.substring(last+1); System.out.println(s4); /*获取除扩展名外的文件名称*/ String s5 = s3.substring(0,last); System.out.println(s5); /*第一次出现的位置*/ System.out.println(s3.indexOf(".")); /*以什么结尾*/ System.out.println(s3.endsWith(".class")); /*以什么开始*/ System.out.println(s3.startsWith("aa")); /*分割*/ String s6 = "AB,BC,CD,DE"; String[] s7 = s6.split(","); System.out.println(Arrays.toString(s7)); /*去除头尾空格*/ String s8 = "\t aaaa bbb \t"; System.out.println(s8.trim()); /*字符串比较 * 从第一个不相同的字符开始比较 * 值是两个字符整型值的差值 */ System.out.println("abc".compareTo("ade")); /* |
|||
Android实现局部图片滑动指引效果 | android实现局部图片滑动指引效果 | ||
我先简要介绍其实现原理: 在布局页面中将设置成局部,限制其高度,然后为滑动的图片集合生成布局界面,并在代码中设置相应的数据适配器和监听事件。在切换事件监听器中更改相应的圆点图片和显示标题,由于滑动图片下方的界面不需要改变内容,所以很很容易内容超过屏幕,所以需要设置ScrollView以在内容比较多时显示滚动条,我会在下面介绍如何让ViewPager和ScrollView结合使用。 先看下android.support.v4.view.ViewPager在布局界面中的核心代码: 复制代码 <android.support.v4.view.ViewPager android:id="@+id/image_slide_page" android:layout_width="fill_parent" android:layout_height="180dip" android:focusable="true" /> 复制代码 在程序结构中,MainActivity.java是启动的Activity,而TopicNews.java是显示头条的Acitivity。在显示时,我们需要将TopicNews.java中的对象进行初始化设置,如下代码: 复制代码 /** * 初始化 */ private void initeViews(){ // 滑动图片区域 imagePageViews = new ArrayList<View>(); LayoutInflater inflater = getLayoutInflater(); main = (ViewGroup)inflater.inflate(R.layout.page_topic_news, null); viewPager = (ViewPager) main.findViewById(R.id.image_slide_page); // 圆点图片区域 parser = new NewsXmlParser(); int length = parser.getSlideImages().length; imageCircleViews = new ImageView[length]; imageCircleView = (ViewGroup) main.findViewById(R.id.layout_circle_images); slideLayout = new SlideImageLayout(TopicNews.this); slideLayout.setCircleImageLayout(length); for(int i = 0;i < length;i++){ imagePageViews.add(slideLayout.getSlideImageLayout(parser.getSlideImages()[i])); imageCircleViews[i] = slideLayout.getCircleImageLayout(i); imageCircleView.addView(slideLayout.getLinearLayout(imageCircleViews[i], 10, 10)); } // 设置默认的滑动标题 tvSlideTitle = (TextView) main.findViewById(R.id.tvSlideTitle); tvSlideTitle.setText(parser.getSlideTitles()[0]); setContentView(main); // 设置ViewPager viewPager.setAdapter(new SlideImageAdapter()); viewPager.setOnPageChangeListener(new ImagePageChangeListener()); } 复制代码 以上对象的声明代码如下所示: 复制代码 // 滑动图片的集合 private ArrayList<View> imagePageViews = null; private ViewGroup main = null; private ViewPager viewPager = null; // 当前ViewPager索引 private int pageIndex = 0; // 包含圆点图片的View private ViewGroup imageCircleView = null; private ImageView[] imageCircleViews = null; // 滑动标题 private TextView tvSlideTitle = null; // 布局设置类 private SlideImageLayout slideLayout = null; // 数据解析类 private NewsXmlParser parser = null; 复制代码 由于在显示头条的Activity即TopicNews中,设置布局文件不是直接设置的,也就是通过inflate将Layout转化为View控件的,所以在使用page_topic_news.xml中的View时,需要通过main.findViewById(),即如下代码所示: main = (ViewGroup)inflater.inflate(R.layout.page_topic_news, null); viewPager = (ViewPager) main.findViewById(R.id.image_slide_page); 而不能像这样直接使用: viewPager = (ViewPager) findViewById(R.id.image_slide_page); 这点大家在使用时需要注意。 NewsXmlParser类是用于对显示的数据进行解析,由于本示例只是一个演示示例,所以在这个类里我只是设置一些要显示的固定数据,没有设置动态数据,这点明白就可以,代码如下: 复制代码 package com.image.indicator.parser; import java.io.InputStream; import java.util.HashMap; import java.util.List; import org.xmlpull.v1.XmlPullParser; import android.util.Xml; import com.image.indicator.R; import com.image.indicator.entity.News; import com.image.indicator.utility.FileAccess; /** * 解析新闻数据列表 * @Description: 解析新闻数据列表,这里只是个示例,具体地不再实现。 * @File: NewsXmlParser.java * @Package com.image.indicator.parser * @Author Hanyonglu * @Date 2012-6-18 下午02:31:26 * @Version V1.0 */ public class NewsXmlParser { // 新闻列表 private List<HashMap<String, News>> newsList = null; // 滑动图片的集合,这里设置成了固定加载,当然也可动态加载。 private int[] slideImages = { R.drawable.image01, R.drawable.image02, R.drawable.image03, R.drawable.image04, R.drawable.image05}; // 滑动标题的集合 private int[] slideTitles = { R.string.title1, R.string.title2, R.string.title3, R.string.title4, R.string.title5, }; // 滑动链接的集合 private String[] slideUrls = { "http://mobile.csdn.net/a/20120616/2806676.html", "http://cloud.csdn.net/a/20120614/2806646.html", "http://mobile.csdn.net/a/20120613/2806603.html", "http://news.csdn.net/a/20120612/2806565.html", "http://mobile.csdn.net/a/20120615/2806659.html", }; public int[] getSlideImages(){ return slideImages; } public int[] getSlideTitles(){ return slideTitles; } public String[] getSlideUrls(){ return slideUrls; } /** * 获取XmlPullParser对象 * @param result * @return */ private XmlPullParser getXmlPullParser(String result){ XmlPullParser parser = Xml.newPullParser(); InputStream inputStream = FileAccess.String2InputStream(result); try { parser.setInput(inputStream, "UTF-8"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return parser; } public int getNewsListCount(String result){ int count = -1; try { XmlPullParser parser = getXmlPullParser(result); int event = parser.getEventType();//产生第一个事件 while(event != XmlPullParser.END_DOCUMENT){ switch(event){ case XmlPullParser.START_DOCUMENT: break; case XmlPullParser.START_TAG://判断当前事件是否是标签元素开始事件 if("count".equals(parser.getName())){//判断开始标签元素是否是count count = Integer.parseInt(parser.nextText()); } break; case XmlPullParser.END_TAG://判断当前事件是否是标签元素结束事件 // if("count".equals(parser.getName())){//判断开始标签元素是否是count // count = Integer.parseInt(parser.nextText()); // } break; } event = parser.next();//进入下一个元素并触发相应事件 } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } // 无返回值,则返回-1 return count; } } 复制代码 关于NewsXmlParser这个类,实现比较简单,不再详述,有兴趣的朋友可以在开发过程中将其设置成动态数据并进行解析。 刚才在上面介绍其实现原理时,我提到需要设置滑动图片集合的布局界面,那么如何设置其布局呢?这里我们需要用到SlideImageLayout。 SlideImageLayout类是用于生成滑动图片区域布局和圆点图片布局的类。我在上面的代码中(即在TopicNews.java的初始化方法initeViews())使用for循环设置滑动图片及圆点图片的布局。在循环中就用到了getSlideImageLayout()、getCircleImageLayout()和getLinearLayout()这几个方法。下面分别看下其功能,先看下getSlideImageLayout()实现代码: 复制代码 /** * 生成滑动图片区域布局 * @param index * @return */ public View getSlideImageLayout(int index){ // 包含TextView的LinearLayout LinearLayout imageLinerLayout = new LinearLayout(activity); LinearLayout.LayoutParams imageLinerLayoutParames = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1); ImageView iv = new ImageView(activity); iv.setBackgroundResource(index); iv.setOnClickListener(new ImageOnClickListener()); imageLinerLayout.addView(iv,imageLinerLayoutParames); imageList.add(iv); return imageLinerLayout; } 复制代码 由于滑动图片一般需要设置其链接或是相应的ID,以便在点击时转向相应的Activity,显示相应的内容或详细信息。这里我没有过多的设置,只是在点击时显示标题及链接地址,代码如下: 复制代码 // 滑动页面点击事件监听器 private class ImageOnClickListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(activity, parser.getSlideTitles()[pageIndex], Toast.LENGTH_SHORT).show(); Toast.makeText(activity, parser.getSlideUrls()[pageIndex], Toast.LENGTH_SHORT).show(); } } 复制代码 getCircleImageLayout()方法主要是为圆点图片生成相应的ImageView对象,代码如下: 复制代码 /** * 生成圆点图片区域布局对象 * @param index * @return */ public ImageView getCircleImageLayout(int index){ imageView = new ImageView(activity); imageView.setLayoutParams(new LayoutParams(10,10)); imageView.setScaleType(ScaleType.FIT_XY); imageViews[index] = imageView; if (index == 0) { //默认选中第一张图片 imageViews[index].setBackgroundResource(R.drawable.dot_selected); } else { imageViews[index].setBackgroundResource(R.drawable.dot_none); } return imageViews[index]; } 复制代码 getLinearLayout()方法则是为圆点图片添加相应的LinearLayout布局,以便设置圆点图片之间的距离,代码如下: 复制代码 /** * 获取LinearLayout * @param view * @param width * @param height * @return */ public View getLinearLayout(View view,int width,int height){ LinearLayout linerLayout = new LinearLayout(activity); LinearLayout.LayoutParams linerLayoutParames = new LinearLayout.LayoutParams( width, height, 1); // 这里最好也自定义设置,有兴趣的自己设置。 linerLayout.setPadding(10, 0, 10, 0); linerLayout.addView(view, linerLayoutParames); return linerLayout; } 复制代码 getCircleImageLayout()和getLinearLayout()方法在NewsTopic.java中for循环的结构中结合代码如下: imageCircleViews[i] = slideLayout.getCircleImageLayout(i); imageCircleView.addView(slideLayout.getLinearLayout(imageCircleViews[i], 10, 10)); 这两个方法结合使用便能优美地实现其圆点图片的布局。 以上是关于NewsXmlParser和SlideImageLayout两个类的介绍,下面让我们再回到TopicNews类中继续介绍相关知识。在TopicNews中进行对象初始化(initeViews()方法)以后,还需要设置ViewPager对象中的数据适配器和监听事件。ViewPager中数据适配器的代码如下: 复制代码 // 滑动图片数据适配器 private class SlideImageAdapter extends PagerAdapter { @Override public int getCount() { return imagePageViews.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public int getItemPosition(Object object) { // TODO Auto-generated method stub return super.getItemPosition(object); } @Override public void destroyItem(View arg0, int arg1, Object arg2) { // TODO Auto-generated method stub ((ViewPager) arg0).removeView(imagePageViews.get(arg1)); } @Override public Object instantiateItem(View arg0, int arg1) { // TODO Auto-generated method stub ((ViewPager) arg0).addView(imagePageViews.get(arg1)); return imagePageViews.get(arg1); } @Override public void restoreState(Parcelable arg0, ClassLoader arg1) { // TODO Auto-generated method stub } @Override public Parcelable saveState() { // TODO Auto-generated method stub return null; } @Override public void startUpdate(View arg0) { // TODO Auto-generated method stub } @Override public void finishUpdate(View arg0) { // TODO Auto-generated method stub } } 复制代码 而ViewPager的事件监听器代码如下: 复制代码 // 滑动页面更改事件监听器 private class ImagePageChangeListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } @Override public void onPageSelected(int index) { pageIndex = index; slideLayout.setPageIndex(index); tvSlideTitle.setText(parser.getSlideTitles()[index]); for (int i = 0; i < imageCircleViews.length; i++) { imageCircleViews[index].setBackgroundResource(R.drawable.dot_selected); if (index != i) { imageCircleViews[i].setBackgroundResource(R.drawable.dot_none); } } } } 复制代码 事件监听器中主要在回调函数onPageSelected(int index)中变换标题和圆点图片。 由于滑动区域下方的内容是不变的,也就是不滑动的,正如在我在上面提到的,内容可能会超出屏幕的范围,所以我们需要使用ScrollView以便内容过多的时候显示滚动条。可能一部分朋友会想到,要显示滚动条我也知道使用ScrollView。我想在这里说的是,这里即有ViewPager控件,也有ScrollView,如果两个View单独使用不会有什么问题。然而不幸的是,两个一结合使用就出现了问题。什么问题呢?就是在滑动图片时出现反弹的现象,就是在滑动时很难滑动,我滑动时感觉很吃力,而且图片就是滑动不过去,这个就是两个View之间的冲突,因为两个View都是滑动的View,都会计算相应的位置和判断相应的距离。 我们如何来解决这个冲突呢?这里我们需要重写ScrollView的onInterceptTouchEvent()回调函数。需要在程序里新加一个ScrollViewExtend类并继承自ScrollView,下面是其代码: 复制代码 package com.image.indicator.control; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ScrollView; /** * 能够兼容ViewPager的ScrollView * @Description: 解决了ViewPager在ScrollView中的滑动反弹问题 * @File: ViewPagerCompatScrollView.java * @Package com.image.indicator.control * @Author Hanyonglu * @Date 2012-6-18 下午01:34:50 * @Version V1.0 */ public class ScrollViewExtend extends ScrollView { // 滑动距离及坐标 private float xDistance, yDistance, xLast, yLast; public ScrollViewExtend(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if(xDistance > yDistance){ return false; } } return super.onInterceptTouchEvent(ev); } } 复制代码 然后在我们的布局代码中添加这个扩展的View,如下代码: 复制代码 <com.image.indicator.control.ScrollViewExtend android:layout_width="match_parent" android:layout_height="fill_parent"> …… </com.image.indicator.control.ScrollViewExtend> 复制代码 以上的操作便可解决ViewPager和ScrollView之间冲突问题,这样便可使用滚动条顺利显示下方不变的内容。在这里再次给大家说明一下,由于本示例只是个演示示例,所以在滑动图片的下方,我只是用了一张图片固定地显示头条Activity的下方。当然有需要的朋友,可以将其进行改造,将滑动图片的下方区域添加个ListView等View之类的以显示相应要求的信息。 一些朋友可能会注意到,在滑动图片区域的下方有一段透明的效果,如下图所示: 这个实现也不难,只是在相应的布局代码中添加background属性即可,如下: android:background="#55000000" 当然,透明度的设置有个范围,有兴趣的朋友到网上查找一下,这里不再详述。 本示例除了实现Android局部图片滑动指引效果以外,还实现了上方导航菜单切换的效果,关于这个效果并不稀奇,因为网上有一些人已经实现该功能。不过在这里,我跟他们做不太一样的是,点击上方的新闻分类时灵敏度比较好,也就是说点中的概率比较大。因为上方的新闻分类文字比较小,要想点中有时不是件容易的事。下面简要说一下其实现过程及相应的代码。 由于要在点击新闻类别时背景图片需要动画效果,所以我添加了一个类:ImageAnimatioin,用于处理图片移动时动画效果。其代码如下: 复制代码 /** * 设置图像移动动画效果 * @param v * @param startX * @param toX * @param startY * @param toY */ public static void SetImageSlide(View v, int startX, int toX, int startY, int toY) { TranslateAnimation anim = new TranslateAnimation(startX, toX, startY, toY); anim.setDuration(100); anim.setFillAfter(true); v.startAnimation(anim); } 复制代码 下面展示一下点击新闻类别时的事件监听器中的代码,因为在这个过程中需要计算移动图片的位置和切换下面的主体内容,如下: 复制代码 // 新闻分类事件监听器 private class ItemOnclickListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub itemWidth = findViewById(R.id.layout).getWidth(); switch (v.getId()) { case R.id.tv_title_news: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, 0, 0, 0); startX = 0; tvSelectedItem.setText(R.string.title_news_category_tops); // 显示头条信息 intent.setClass(MainActivity.this, TopicNews.class); vNewsMain = getLocalActivityManager().startActivity( "TopicNews", intent).getDecorView(); break; case R.id.tv_title_info: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth, 0, 0); startX = itemWidth; tvSelectedItem.setText(R.string.title_news_category_info); // 显示资讯信息 intent.setClass(MainActivity.this, InfoNews.class); vNewsMain = getLocalActivityManager().startActivity( "InfoNews", intent).getDecorView(); break; case R.id.tv_title_blog: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth * 2, 0, 0); startX = itemWidth * 2; tvSelectedItem.setText(R.string.title_news_category_blog); // 显示博客信息 intent.setClass(MainActivity.this, BlogNews.class); vNewsMain = getLocalActivityManager().startActivity( "BlogNews", intent).getDecorView(); break; case R.id.tv_title_magazine: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth * 3, 0, 0); startX = itemWidth * 3; tvSelectedItem.setText(R.string.title_news_category_magazine); // 显示杂志信息 intent.setClass(MainActivity.this, MagazineNews.class); vNewsMain = getLocalActivityManager().startActivity( "MagazineNews", intent).getDecorView(); break; case R.id.tv_title_domain: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth * 4, 0, 0); startX = itemWidth * 4; tvSelectedItem.setText(R.string.title_news_category_domain); // 显示业界信息 intent.setClass(MainActivity.this, DomainNews.class); vNewsMain = getLocalActivityManager().startActivity( "DomainNews", intent).getDecorView(); break; case R.id.tv_title_more: ImageAnimatioin.SetImageSlide(tvSelectedItem, startX, itemWidth * 5, 0, 0); startX = itemWidth * 5; tvSelectedItem.setText(R.string.title_news_category_more); // 显示更多信息 intent.setClass(MainActivity.this, MoreNews.class); vNewsMain = getLocalActivityManager().startActivity( "MoreNews", intent).getDecorView(); break; default: break; } // 更换Layout中的新闻主体 rlNewsMain.removeAllViews(); rlNewsMain.addView(vNewsMain, params); } } 复制代码 |
|||
Android实现数据存储技术 | android实现数据存储技术 | ||
Android实现数据存储技术 本文介绍Android中的5种数据存储方式。 数据存储在开发中是使用最频繁的,在这里主要介绍Android平台中实现数据存储的5种方式,分别是: 1 使用SharedPreferences存储数据; 2 文件存储数据; 3 SQLite数据库存储数据; 4 使用ContentProvider存储数据; 5 网络存储数据; 下面将为大家一一详细介绍。 第一种: 使用SharedPreferences存储数据 SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceState保存一般使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。 它是什么样的处理方式呢? SharedPreferences类似过去Windows系统上的ini配置文件,但是它分为多种权限,可以全局共享访问,android123提示最终是以xml方式来保存,整体效率来看不是特别的高,对于常规的轻量级而言比SQLite要好不少,如果真的存储量不大可以考虑自己定义文件格式。xml 处理时Dalvik会通过自带底层的本地XML Parser解析,比如XMLpull方式,这样对于内存资源占用比较好。 它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。 其存储位置在/data/data/<包名>/shared_prefs目录下。 SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。 实现SharedPreferences存储的步骤如下: 一、根据Context获取SharedPreferences对象 二、利用edit()方法获取Editor对象。 三、通过Editor对象存储key-value键值对数据。 四、通过commit()方法提交数据。 下面是示例代码: 复制代码 public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //获取SharedPreferences对象 Context ctx = MainActivity.this; SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE); //存入数据 Editor editor = sp.edit(); editor.putString("STRING_KEY", "string"); editor.putInt("INT_KEY", 0); editor.putBoolean("BOOLEAN_KEY", true); editor.commit(); //返回STRING_KEY的值 Log.d("SP", sp.getString("STRING_KEY", "none")); //如果NOT_EXIST不存在,则返回值为"none" Log.d("SP", sp.getString("NOT_EXIST", "none")); } } 复制代码 这段代码执行过后,即在/data/data/com.test/shared_prefs目录下生成了一个SP.xml文件,一个应用可以创建多个这样的xml文件。 SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其职能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。 第二种: 文件存储数据 关于文件存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。 文件可用来存放大量数据,如文本、图片、音频等。 默认位置:/data/data/<包>/files/***.***。 代码示例: 复制代码 public void save() { try { FileOutputStream outStream=this.openFileOutput("a.txt",Context.MODE_WORLD_READABLE); outStream.write(text.getText().toString().getBytes()); outStream.close(); Toast.makeText(MyActivity.this,"Saved",Toast.LENGTH_LONG).show(); } catch (FileNotFoundException e) { return; } catch (IOException e){ return ; } } 复制代码 openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。 创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。 openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE = 0 Context.MODE_APPEND = 32768 Context.MODE_WORLD_READABLE = 1 Context.MODE_WORLD_WRITEABLE = 2 Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。 Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。 MODE_WORLD_READABLE:表示当前文件可以被其他应用读取; MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。 如果希望文件被其他应用读和写,可以传入: openFileOutput("itcast.txt", Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE); android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。 除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。 读取文件示例: 复制代码 public void load() { try { FileInputStream inStream=this.openFileInput("a.txt"); ByteArrayOutputStream stream=new ByteArrayOutputStream(); byte[] buffer=new byte[1024]; int length=-1; while((length=inStream.read(buffer))!=-1) { stream.write(buffer,0,length); } stream.close(); inStream.close(); text.setText(stream.toString()); Toast.makeText(MyActivity.this,"Loaded",Toast.LENGTH_LONG).show(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e){ return ; } } 复制代码 对于私有文件只能被创建该文件的应用访问,如果希望文件能被其他应用读和写,可以在创建文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。 Activity还提供了getCacheDir()和getFilesDir()方法: getCacheDir()方法用于获取/data/data/<package name>/cache目录 getFilesDir()方法用于获取/data/data/<package name>/files目录。 把文件存入SDCard 使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,一般手机的存储空间不是很大,存放些小文件还行,如果要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,我们可以把它存放在SDCard。 SDCard是干什么的?你可以把它看作是移动硬盘或U盘。 在模拟器中使用SDCard,你需要先创建一张SDCard卡(当然不是真的SDCard,只是镜像文件)。 创建SDCard可以在Eclipse创建模拟器时随同创建,也可以使用DOS命令进行创建,如下: 在Dos窗口中进入android SDK安装路径的tools目录,输入以下命令创建一张容量为2G的SDCard,文件后缀可以随便取,建议使用.img: mksdcard 2048M D:\AndroidTool\sdcard.img 在程序中访问SDCard,你需要申请访问SDCard的权限。 在AndroidManifest.xml中加入访问SDCard的权限如下: <!-- 在SDCard中创建与删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SDCard写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 要往SDCard存放文件,程序必须先判断手机是否装有SDCard,并且可以进行读写。 注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限。 复制代码 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录 File saveFile = new File(sdCardDir, “a.txt”); FileOutputStream outStream = new FileOutputStream(saveFile); outStream.write("test".getBytes()); outStream.close(); } 复制代码 Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。 Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,当然要获取SDCard的目录,你也可以这样写: File sdCardDir = new File("/sdcard"); //获取SDCard目录 File saveFile = new File(sdCardDir, "itcast.txt"); //上面两句代码可以合成一句: File saveFile = new File("/sdcard/a.txt"); FileOutputStream outStream = new FileOutputStream(saveFile); outStream.write("test".getBytes()); outStream.close(); 第三种: SQLite数据库存储数据 SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。许多开源项目((Mozilla, PHP, Python)都使用了 SQLite.SQLite 由以下几个组件组成:SQL 编译器、内核、后端以及附件。SQLite 通过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展 SQLite 的内核变得更加方便。 特点: 面向资源有限的设备, 没有服务器进程, 所有数据存放在同一文件中跨平台, 可自由复制。 SQLite 内部结构: SQLite 基本上符合 SQL-92 标准,和其他的主要 SQL 数据库没什么区别。它的优点就是高效,Android 运行时环境包含了完整的 SQLite。 SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite 称这为“弱类型”(manifest typing.)。 此外,SQLite 不支持一些标准的 SQL 功能,特别是外键约束(FOREIGN KEY constrains),嵌套 transcaction 和 RIGHT OUTER JOIN 和 FULL OUTER JOIN, 还有一些 ALTER TABLE 功能。 除了上述功能外,SQLite 是一个完整的 SQL 系统,拥有完整的触发器,交易等等。 Android 集成了 SQLite 数据库 Android 在运行时(run-time)集成了 SQLite,所以每个 Android 应用程序都可以使用 SQLite 数据库。 对于熟悉 SQL 的开发人员来时,在 Android 开发中使用 SQLite 相当简单。但是,由于 JDBC 会消耗太多的系统资源,所以 JDBC 对于手机这种内存受限设备来说并不合适。因此,Android 提供了一些新的 API 来使用 SQLite 数据库,Android 开发中,程序员需要学使用这些 API。 数据库存储在 data/< 项目文件夹 >/databases/ 下。 Android 开发中使用 SQLite 数据库 Activites 可以通过 Content Provider 或者 Service 访问一个数据库。 下面会详细讲解如果创建数据库,添加数据和查询数据库。 创建数据库 Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。 Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper 类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。 SQLiteOpenHelper 的子类,至少需要实现三个方法: 1 构造函数,调用父类 SQLiteOpenHelper 的构造函数。这个方法需要四个参数:上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null),一个代表你正在使用的数据库模型版本的整数。 2 onCreate()方法,它需要一个 SQLiteDatabase 对象作为参数,根据需要对这个对象填充表和初始化数据。 3 onUpgrage() 方法,它需要三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号,这样你就可以清楚如何把一个数据库从旧的模型转变到新的模型。 下面示例代码展示了如何继承 SQLiteOpenHelper 创建数据库: 复制代码 public class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context, String name, CursorFactory cursorFactory, int version) { super(context, name, cursorFactory, version); } @Override public void onCreate(SQLiteDatabase db) { // TODO 创建数据库后,对数据库的操作 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO 更改数据库版本的操作 } @Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); // TODO 每次成功打开数据库后首先被执行 } } 复制代码 接下来讨论具体如何创建表、插入数据、删除表等等。调用 getReadableDatabase() 或 getWriteableDatabase() 方法,你可以得到 SQLiteDatabase 实例,具体调用那个方法,取决于你是否需要改变数据库的内容: db=(new DatabaseHelper(getContext())).getWritableDatabase(); return (db == null) ? false : true; 上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就可以查询或者修改数据库。 当你完成了对数据库的操作(例如你的 Activity 已经关闭),需要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库连接。 创建表和索引 为了创建表和索引,需要调用 SQLiteDatabase 的 execSQL() 方法来执行 DDL 语句。如果没有异常,这个方法没有返回值。 例如,你可以执行如下代码: db.execSQL("CREATE TABLE mytable (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, value REAL);"); 这条语句会创建一个名为 mytable 的表,表有一个列名为 _id,并且是主键,这列的值是会自动增长的整数(例如,当你插入一行时,SQLite 会给这列自动赋值),另外还有两列:title( 字符 ) 和 value( 浮点数 )。 SQLite 会自动为主键列创建索引。 通常情况下,第一次创建数据库时创建了表和索引。 如果你不需要改变表的 schema,不需要删除表和索引 . 删除表和索引,需要使用 execSQL() 方法调用 DROP INDEX 和 DROP TABLE 语句。 给表添加数据 上面的代码,已经创建了数据库和表,现在需要给表添加数据。有两种方法可以给表添加数据。 像上面创建表一样,你可以使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于所有不返回结果的 SQL 语句。 例如: db.execSQL("INSERT INTO widgets (name, inventory)"+ "VALUES ('Sprocket', 5)"); 另一种方法是使用 SQLiteDatabase 对象的 insert(), update(), delete() 方法。这些方法把 SQL 语句的一部分作为参数。 示例如下: ContentValues cv=new ContentValues(); cv.put(Constants.TITLE, "example title"); cv.put(Constants.VALUE, SensorManager.GRAVITY_DEATH_STAR_I); db.insert("mytable", getNullColumnHack(), cv); update()方法有四个参数,分别是表名,表示列名和值的 ContentValues 对象,可选的 WHERE 条件和可选的填充 WHERE 语句的字符串,这些字符串会替换 WHERE 条件中的“?”标记。 update() 根据条件,更新指定列的值,所以用 execSQL() 方法可以达到同样的目的。 WHERE 条件和其参数和用过的其他 SQL APIs 类似。 例如: String[] parms=new String[] {"this is a string"}; db.update("widgets", replacements, "name=?", parms); delete() 方法的使用和 update() 类似,使用表名,可选的 WHERE 条件和相应的填充 WHERE 条件的字符串。 查询数据库 类似 INSERT, UPDATE, DELETE,有两种方法使用 SELECT 从 SQLite 数据库检索数据。 1 .使用 rawQuery() 直接调用 SELECT 语句; 使用 query() 方法构建一个查询。 Raw Queries 正如 API 名字,rawQuery() 是最简单的解决方法。通过这个方法你就可以调用 SQL SELECT 语句。 例如: Cursor c=db.rawQuery( "SELECT name FROM sqlite_master WHERE type='table' AND name='mytable'", null); 在上面例子中,我们查询 SQLite 系统表(sqlite_master)检查 table 表是否存在。返回值是一个 cursor 对象,这个对象的方法可以迭代查询结果。 如果查询是动态的,使用这个方法就会非常复杂。 例如,当你需要查询的列在程序编译的时候不能确定,这时候使用 query() 方法会方便很多。 Regular Queries query() 方法用 SELECT 语句段构建查询。SELECT 语句内容作为 query() 方法的参数,比如:要查询的表名,要获取的字段名,WHERE 条件,包含可选的位置参数,去替代 WHERE 条件中位置参数的值,GROUP BY 条件,HAVING 条件。 除了表名,其他参数可以是 null。所以,以前的代码段可以可写成: String[] columns={"ID", "inventory"}; String[] parms={"snicklefritz"}; Cursor result=db.query("widgets", columns, "name=?",parms, null, null, null); 使用游标 不管你如何执行查询,都会返回一个 Cursor,这是 Android 的 SQLite 数据库游标, 使用游标,你可以: 通过使用 getCount() 方法得到结果集中有多少记录; 通过 moveToFirst(), moveToNext(), 和 isAfterLast() 方法遍历所有记录; 通过 getColumnNames() 得到字段名; 通过 getColumnIndex() 转换成字段号; 通过 getString(),getInt() 等方法得到给定字段当前记录的值; 通过 requery() 方法重新执行查询得到游标; 通过 close() 方法释放游标资源; 例如,下面代码遍历 mytable 表: 复制代码 Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable"); result.moveToFirst(); while (!result.isAfterLast()) { int id=result.getInt(0); String name=result.getString(1); int inventory=result.getInt(2); // do something useful with these result.moveToNext(); } result.close(); 复制代码 在 Android 中使用 SQLite 数据库管理工具 在其他数据库上作开发,一般都使用工具来检查和处理数据库的内容,而不是仅仅使用数据库的 API。 使用 Android 模拟器,有两种可供选择的方法来管理数据库。 首先,模拟器绑定了 sqlite3 控制台程序,可以使用 adb shell 命令来调用他。只要你进入了模拟器的 shell,在数据库的路径执行 sqlite3 命令就可以了。 数据库文件一般存放在: /data/data/your.app.package/databases/your-db-name 如果你喜欢使用更友好的工具,你可以把数据库拷贝到你的开发机上,使用 SQLite-aware 客户端来操作它。这样的话,你在一个数据库的拷贝上操作,如果你想要你的修改能反映到设备上,你需要把数据库备份回去。 把数据库从设备上考出来,你可以使用 adb pull 命令(或者在 IDE 上做相应操作)。 存储一个修改过的数据库到设备上,使用 adb push 命令。 一个最方便的 SQLite 客户端是 FireFox SQLite Manager 扩展,它可以跨所有平台使用。 下图是SQLite Manager工具: 如果你想要开发 Android 应用程序,一定需要在 Android 上存储数据,使用 SQLite 数据库是一种非常好的选择。 第四种: 使用ContentProvider存储数据 Android这个系统和其他的操作系统还不太一样,我们需要记住的是,数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。那这个时候有读者就会提出问题,难道两个程序之间就没有办法对于数据进行交换?Android这么优秀的系统不会让这种情况发生的。解决这个问题主要靠ContentProvider。一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProviders是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。 Content Provider提供了一种多应用间数据共享的方式,比如:联系人信息可以被多个应用程序访问。 Content Provider是个实现了一组用于提供其他应用程序存取数据的标准方法的类。 应用程序可以在Content Provider中执行如下操作: 查询数据 修改数据 添加数据 删除数据 标准的Content Provider: Android提供了一些已经在系统中实现的标准Content Provider,比如联系人信息,图片库等等,你可以用这些Content Provider来访问设备上存储的联系人信息,图片等等。 查询记录: 在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。 以下是一些示例URI: content://media/internal/images 这个URI将返回设备上存储的所有图片 content://contacts/people/ 这个URI将返回设备上的所有联系人信息 content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录) 尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,参见下例: MediaStore.Images.Media.INTERNAL_CONTENT_URI Contacts.People.CONTENT_URI 因此,如上面content://contacts/people/45这个URI就可以写成如下形式: Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45); 然后执行数据查询: Cursor cur = managedQuery(person, null, null, null); 这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据: 复制代码 package com.wissen.testApp; public class ContentProviderDemo extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); displayRecords(); } private void displayRecords() { //该数组中包含了所有要返回的字段 String columns[] = new String[] { People.NAME, People.NUMBER }; Uri mContacts = People.CONTENT_URI; Cursor cur = managedQuery( mContacts, columns, // 要返回的数据字段 null, // WHERE子句 null, // WHERE 子句的参数 null // Order-by子句 ); if (cur.moveToFirst()) { String name = null; String phoneNo = null; do { // 获取字段的值 name = cur.getString(cur.getColumnIndex(People.NAME)); phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER)); Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show(); } while (cur.moveToNext()); } } } 复制代码 上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。 修改记录: 我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法: 复制代码 private void updateRecord(int recNo, String name) { Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo); ContentValues values = new ContentValues(); values.put(People.NAME, name); getContentResolver().update(uri, values, null, null); } 复制代码 现在你可以调用上面的方法来更新指定记录: updateRecord(10, ”XYZ”); //更改第10条记录的name字段值为“XYZ” 添加记录: 要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。 上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加: 复制代码 private void insertRecords(String name, String phoneNo) { ContentValues values = new ContentValues(); values.put(People.NAME, name); Uri uri = getContentResolver().insert(People.CONTENT_URI, values); Log.d(”ANDROID”, uri.toString()); Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY); values.clear(); values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE); values.put(People.NUMBER, phoneNo); getContentResolver().insert(numberUri, values); } 复制代码 这样我们就可以调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。 删除记录: Content Provider中的getContextResolver.delete()方法可以用来删除记录。 下面的记录用来删除设备上所有的联系人信息: private void deleteRecords() { Uri uri = People.CONTENT_URI; getContentResolver().delete(uri, null, null); } 你也可以指定WHERE条件语句来删除特定的记录: getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null); 这将会删除name为‘XYZ XYZ’的记录。 创建Content Provider: 至此我们已经知道如何使用Content Provider了,现在让我们来看下如何自己创建一个Content Provider。 要创建我们自己的Content Provider的话,我们需要遵循以下几步: 1. 创建一个继承了ContentProvider父类的类 2. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如: public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”); 3. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。 4. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其他数据库一样。但是,你必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。 5. 如果你要存储字节型数据,比如位图文件等,那保存该数据的数据列其实是一个表示实际保存文件的URI字符串,客户端通过它来读取对应的文件数据,处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源,如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。 6. 声明public static String型的变量,用于指定要从游标处返回的数据列。 7. 查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。 8. 在AndroidMenifest.xml中使用标签来设置Content Provider。 9. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。 MIME类型有两种形式: 一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式: vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的MIME类型) 比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。 vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的MIME类型) 比如, 一个请求所有列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。 下列代码将创建一个Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据): 复制代码 package com.wissen.testApp; public class MyUsers { public static final String AUTHORITY = “com.wissen.MyContentProvider”; // BaseColumn类中已经包含了 _id字段 public static final class User implements BaseColumns { public static final Uri CONTENT_URI = Uri.parse(”content://com.wissen.MyContentProvider”); // 表数据列 public static final String USER_NAME = “USER_NAME”; } } 复制代码 上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的Content Provider类: 复制代码 package com.wissen.testApp.android; public class MyContentProvider extends ContentProvider { private SQLiteDatabase sqlDB; private DatabaseHelper dbHelper; private static final String DATABASE_NAME = “Users.db”; private static final int DATABASE_VERSION = 1; private static final String TABLE_NAME = “User”; private static final String TAG = “MyContentProvider”; private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { //创建用于存储数据的表 db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME); onCreate(db); } } @Override public int delete(Uri uri, String s, String[] as) { return 0; } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues contentvalues) { sqlDB = dbHelper.getWritableDatabase(); long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues); if (rowId > 0) { Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build(); getContext().getContentResolver().notifyChange(rowUri, null); return rowUri; } throw new SQLException(”Failed to insert row into ” + uri); } @Override public boolean onCreate() { dbHelper = new DatabaseHelper(getContext()); return (dbHelper == null) ? false : true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteDatabase db = dbHelper.getReadableDatabase(); qb.setTables(TABLE_NAME); Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; } } 复制代码 一个名为MyContentProvider的Content Provider创建完成了,它用于从Sqlite数据库中添加和读取记录。 Content Provider的入口需要在AndroidManifest.xml中配置: 之后,让我们来使用这个定义好的Content Provider: 复制代码 package com.wissen.testApp; public class MyContentDemo extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); insertRecord(”MyUser”); displayRecords(); } private void insertRecord(String userName) { ContentValues values = new ContentValues(); values.put(MyUsers.User.USER_NAME, userName); getContentResolver().insert(MyUsers.User.CONTENT_URI, values); } private void displayRecords() { String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME }; Uri myUri = MyUsers.User.CONTENT_URI; Cursor cur = managedQuery(myUri, columns,null, null, null ); if (cur.moveToFirst()) { String id = null; String userName = null; do { id = cur.getString(cur.getColumnIndex(MyUsers.User._ID)); userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME)); Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show(); } while (cur.moveToNext()); } } } 复制代码 上面的类将先向数据库中添加一条用户数据,然后显示数据库中所有的用户数据。 第五种: 网络存储数据 前面介绍的几种存储都是将数据存储在本地设备上,除此之外,还有一种存储(获取)数据的方式,通过网络来实现数据的存储和获取。 我们可以调用WebService返回的数据或是解析HTTP协议实现网络数据交互。 具体需要熟悉java.net.*,Android.net.*这两个包的内容,在这就不赘述了,请大家参阅相关文档。 下面是一个通过地区名称查询该地区的天气预报,以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务。 代码如下: 复制代码 package com.android.weather; 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.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; public class MyAndroidWeatherActivity extends Activity { //定义需要获取的内容来源地址 private static final String SERVER_URL = "http://www.webservicex.net/WeatherForecast.asmx/GetWeatherByPlaceName"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); HttpPost request = new HttpPost(SERVER_URL); //根据内容来源地址创建一个Http请求 // 添加一个变量 List<NameValuePair> params = new ArrayList<NameValuePair>(); // 设置一个地区名称 params.add(new BasicNameValuePair("PlaceName", "NewYork")); //添加必须的参数 try { //设置参数的编码 request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //发送请求并获取反馈 HttpResponse httpResponse = new DefaultHttpClient().execute(request); // 解析返回的内容 if(httpResponse.getStatusLine().getStatusCode() != 404){ String result = EntityUtils.toString(httpResponse.getEntity()); System.out.println(result); } } catch (Exception e) { e.printStackTrace(); } } } 复制代码 别忘记了在配置文件中设置访问网络权限: <uses-permission android:name="android.permission.INTERNET" /> |
|||
函数的递归调用 | java递归 | ||
package day06; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { Scanner s = new Scanner(System.in); System.out.println("请输入项数:"); int n = s.nextInt(); int sum = 0; /*int prelast = 1;//上一项的上一项 int last = 1;//上一项 for(int i= 3; i<=n ;i++){ int now = prelast+last; sum += now; prelast = last; last = now; }*/ for(int i = 1; i <= n ;i++){ sum+=f(i); } System.out.println(sum); //System.out.println(f(7)); /*int n = 5; long y = g(n); System.out.println(y);*/ } /** * 求n项的值 * @param n * @return */ public static int f(int n){ if(n==1 || n==2) return 1; int y = f(n-1)+f(n-2); return y; } /** * 求阶乘 * @param n * @return */ public static long g(int n){ if(n==1) return 1; long y = g(n-1)*n; return y; } } |
|||
随机产生4位验证码 | java数组 | ||
/** * 随机产生4位验证码 * @return */ public static char[] getchars(){ char[] pool = {'2','3','4','5','6','7','8','a','b','c','d','e','f'}; char[] v = new char[4]; boolean[] used = new boolean[pool.length]; Random r = new Random(); int i = 0; while(true){ int index = r.nextInt(pool.length); if(used[index]){ continue; } v[i++] = pool[index]; used[index] = true; if(i==4)break; } return v; } } |
|||
随机产生双色球 | java数组 | ||
/** * 随机产生双色球 * @return */ public static String[] getBalls(){ String[] pool = { "01","02","03","04","05","06","07","08","09","10", "11","12","13","14","15","16","17","18","19","20", "21","22","23","24","25","26","27","28","29","30", "31","32","33" }; /*最后选出的放入一个数组*/ String[] balls = new String[6]; /*如果一个元素已经放入就不能再被选中 * boolean类型的默认值是false */ boolean[] used = new boolean[pool.length]; int i = 0;//记录下标 balls的下标 Random r = new Random(); while(true){ /*随机得到一个索引*/ int index = r.nextInt(pool.length); /*得到索引判断是否已经被使用*/ if(used[index]){ continue; } balls[i++] = pool[index]; used[index] = true; if(i==6)break; } /*对数组进行排序*/ Arrays.sort(balls); /*加最后一个16选1*/ balls = Arrays.copyOf(balls, balls.length+1); balls[balls.length - 1] = pool[r.nextInt(16)]; return balls; } |
|||
对数组进行扩容 | java数组 | ||
/** * 在数组的制定位置插入一个数组 * @param a * @param pos * @param values * @return */ public static int[] insert(int[] a,int pos,int[] values){ /*对数组进行扩容*/ a = Arrays.copyOf(a, a.length+values.length); /*后移*/ for(int i = a.length - 1; i > pos+values.length-1 ;i--){ a[i] = a[i-values.length]; } /*插入元素的操作*/ for(int i = 0; i < values.length;i++){ a[pos++] = values[i]; } return a; } |
|||
打印杨辉三角的前10行 | java中for循环 | ||
public static void printTri(){ int[][] a = new int[10][10]; for(int i = 0; i < 10;i++){ for(int j = 0; j <= i;j++){ a[i][j]=1; } } for(int i = 2; i < 10;i++){ for(int j = 1; j < i;j++){ a[i][j] = a[i-1][j]+a[i-1][j-1]; } } for(int i = 0; i < 10;i++){ for(int j = 0 ; j <= i ;j++){ System.out.print(a[i][j]+" "); } System.out.println(); } } |
|||
二维数组最大值,最小值,和. | java数组 | ||
package day05; public class P3 { public static void main(String[] args) { int[][] a ={ {1,2,3,4}, {5,6,7,8,9}, {10,11,12} }; /*二维数组的长度是其中含有一维数组的个数*/ System.out.println("二维数组的长度是:"+a.length); /*这个二维数组中含有了3个一维数组 * 第一个一维数组的名字:a[0] * a[1] * a[2] */ /*看一看二维数组中第二个一维数组的长度*/ System.out.println(a[1].length); /*遍历一下上说的一维数组*/ for(int i = 0; i < a[1].length;i++){ System.out.print(a[1][i]+","); } System.out.println(); System.out.println("======================="); /*遍历二维数组中所有一维数组的元素*/ for(int i = 0; i < a.length;i++){ for(int j = 0 ; j < a[i].length;j++){ System.out.print(a[i][j]+","); } System.out.println(); } System.out.println("================================="); /*取最大值,最小值求和*/ int sum = 0,max=a[0][0],min=a[0][0]; for(int i = 0; i < a.length;i++){ for(int j = 0 ; j < a[i].length;j++){ sum+=a[i][j]; if(max < a[i][j]) max = a[i][j]; if(min > a[i][j]) min = a[i][j]; } } System.out.println("和是:"+sum+"最大值是:"+max+"最小值是:"+min); } } |
|||
倒置数组 | java数组 | ||
package day05; import java.util.Arrays; public class Demo1 { public static void main(String[] args) { int[] a = {1,2,3,4,5}; //reverseArray(a);//调用函数传递数组只要传递数组名即可 //System.out.println(Arrays.toString(a)); System.out.println("================="); int[] b = reverseArray1(a); System.out.println(Arrays.toString(b)); b[0] = 10; System.out.println(Arrays.toString(a)); } /** * 倒置数组 * @param n */ public static void reverseArray(int[] n){ for(int i = 0; i < n.length/2;i++){ int t = n[i]; n[i] = n[n.length-i-1]; n[n.length-i-1] = t; } //n[0] = 10; } public static int[] reverseArray1(int[] n){ for(int i = 0; i < n.length/2;i++){ int t = n[i]; n[i] = n[n.length-i-1]; n[n.length-i-1] = t; } return n; } } |
|||
数组的排序 | java数组 | ||
package day05; import java.util.Arrays; public class LianXi2 { public static void main(String[] args) { int[] a = {20,18,35,17,19,43,27}; //sortArray(a); //sortArray1(a); sortArray2(a); System.out.println(Arrays.toString(a)); } /** * 选择排序 * @param a */ public static void sortArray(int[] a){ for(int i = 0; i < a.length-1;i++){ for(int j = i+1;j < a.length;j++){ if(a[i]>a[j]){ int t = a[i]; a[i] = a[j]; a[j] = t; } } } } /** * 冒泡排序 * @param a */ public static void sortArray1(int[] a){ for(int i = 0 ; i < a.length-1;i++){ for(int j = 0;j < a.length-1-i;j++){ if(a[j]>a[j+1]){ int t = a[j]; a[j] = a[j+1]; a[j+1] = t; } } } } /** * 插入排序 * @param a */ public static void sortArray2(int[] a){ int i,j,t ; for(i = 1; i < a.length ;i++){ t = a[i]; /*for(j = i-1; j>=0 ;j--){ if(t < a[j]){ a[j+1] = a[j]; }else{ break; } }*/ for(j = i- 1; j>=0 && t<a[j];j--){ a[j+1]=a[j]; } a[j+1] = t; } } } |
|||
插入value值 (pos从0开始记位) | java数组 | ||
//写一个函数带有三个参数 public static void insert (int[] a,int pos,int value) 在pos的位置插入value值 (pos从0开始记位) package day05; import java.util.Arrays; public class Demo3 { public static void main(String[] args) { /*int[] a = {1,2,3,4}; int[] b = new int[a.length+1]; for(int i = 0 ; i < a.length;i++){ b[i] = a[i]; } a = b; System.out.println(Arrays.toString(a));*/ int[] a = {1,2,3,4,5}; a = insert(a,5,10); System.out.println(Arrays.toString(a)); } public static int[] insert(int[] a,int pos,int value){ a = Arrays.copyOf(a, a.length+1); /*从pos后一个位置 i开始 i+1位置的值就应该=i位置的值*/ /*从最后一个位置开始赋值*/ for(int i = a.length-1;i > pos;i--){ a[i] = a[i-1]; } a[pos] = value; return a; } } |
|||
验证哥德巴赫猜想 | java中for循环 | ||
//在1742年给欧拉的信中哥德巴赫提出了以下猜想:任一大于2的整数都可写成三个质数之和。因现今数学界已经不使用“1也是素数”这个约定,原初猜想的现代陈述为:任一大于5的整数都可写成三个质数之和。欧拉在回信中也提出另一等价版本,即任一大于2的偶数都可写成两个质数之和。今日常见的猜想陈述为欧拉的版本。把命题"任一充分大的偶数都可以表示成为一个素因子个数不超过a个的数与另一个素因子不超过b个的数之和"记作"a+b"。1966年陈景润证明了"1+2"成立,即"任一充分大的偶数都可以表示成二个素数的和,或是一个素数和一个半素数的和"。 package day04; import java.util.Scanner; public class LianXi3 { /** * @param args */ public static void main(String[] args) { Scanner s = new Scanner(System.in); System.out.println("请输入一个大于6的偶数"); int a = s.nextInt(); if(a < 6 || a % 2 !=0){ System.out.println("输入不合法"); return; } /*验证哥德巴赫猜想*/ for(int i = 3 ; i <= a/2;i++){ //把a 拆分成两个数字i a-i if(isPrimeNumber(i)&&isPrimeNumber(a-i)){ System.out.println(a+"="+i+"+"+(a-i)); } } } public static boolean isPrimeNumber(int n){ for(int i = 2; i <= n / 2 ; i++){ if( n % i == 0) return false; } return true; } } |
|||
打印10到1000之间的可逆素数 | java中for循环 | ||
package day04; public class LianXi2 { /** * @param args */ public static void main(String[] args) { for(int i = 11 ;i <= 1000;i+=2){ /*先得到i倒置后的数字*/ int x = reverseNumber(i); if(isPrimeNumber(i) && isPrimeNumber(x)){ System.out.println(i); } } } public static boolean isPrimeNumber(int a){ for(int i = 2; i <= a/2 ; i++){ if(a % i== 0) return false; } return true; } public static int reverseNumber(int n){ int sum = 0; while(n!=0){ sum = sum * 10 + n % 10; n = n/ 10; } return sum; } } |
|||
求1!+2!+3!+...+10! | java中for循环 | ||
package day03; public class LianXi5 { public static void main(String[] args) { long sum = 0, t = 1; for(int i = 1; i <= 10;i++){ t *= i ; // t = t * i; sum += t; } System.out.println(sum); } } |
|||
通过键盘输入一个数字,请对其因式分解 | java中for循环 | ||
package day03; import java.util.Scanner; public class LianXi4 { public static void main(String[] args) { /*通过键盘输入一个数字因式分解*/ Scanner s = new Scanner(System.in); System.out.println("请输入数字:"); int a =s.nextInt(); System.out.print(a+" = "); for(int i = 2; i <= a / 2 ;i++){ if(a % i == 0){ System.out.print(i + " * "); /*接下就不是对a 做因式分解 而是对a/i的那个值做因是分解*/ a = a / i ; i = 1; } } System.out.print(a); } } |
|||
通过键盘输入一个数字,打印其所有的因子 | java中for循环 | ||
package day03; import java.util.Scanner; public class LianXi3 { public static void main(String[] args) { /*打印数的所有因子*/ Scanner s = new Scanner(System.in); System.out.println("请输入数字"); int a = s.nextInt(); /*for(int i = 1 ; i <= a;i++){ if(a % i == 0){ System.out.print(i+","); } }*/ /*一个数字除以大于自己一半并且小于自己的数字肯有余数*/ for(int i = 1; i <= a/2;i++){ if(a % i == 0){ System.out.print(i+","); } } System.out.print(a); } } |
|||
求1到100之间奇数的和 | java中for循环 | ||
int sum = 0;//定义一个变量来接受数值和 /*for(int i = 1; i <= 100;i++){ if(i % 2 != 0){ sum += i; } }*/第一种 for(int i = 1; i <= 100 ; i+= 2){ sum += i; } System.out.println(sum);//第二种 |
|||
求1到100的和 | java中for循环 | ||
package 包名; public class 类名 { public static void main(String[] args) { /*1到100的和 *求和 先定义一个变量初始为0 ,累加 *第一次累加1 *第二次累加2 *第三次累加3 *... *第一百次类累100 */ int sum = 0; for(int i = 1; i <= 100;i++){ sum += i;//sum = sum + i //System.out.println(sum); } System.out.println(sum); } } |
|||
短信基础 | 手机短信基础 | ||
短信相关权限 <!-- 发送消息--> <uses-permission android:name="android.permission.SEND_SMS"/> <!-- 阅读消息--> <uses-permission android:name="android.permission.READ_SMS"/> <!-- 写入消息--> <uses-permission android:name="android.permission.WRITE_SMS" /> <!-- 接收消息 --> <uses-permission android:name="android.permission.RECEIVE_SMS" /> 系统的短信库存在data/data/com.android.providers.telephony/databases/mmssms.db 但真机不可读 URI:content://mms-sms/conversations 相关重要列 thread_id :这个字段很重要,同一个会话中他们的thread_id是一样的,也就是说通过thread_id就可以知道A与B在聊天 还是 A与C在聊天 date :这条消息发送或接收的时间 read: 0 表示未读 1表示已读 type : 1表示接收 2 表示发出 body 表示 消息的内容 ///////////////////////////////////////////////////////////////////// /**发送与接收的广播**/ String SENT_SMS_ACTION = "SENT_SMS_ACTION"; String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; //注册两个用于监控短信情况的广播 public void registerSmsReceiver(){ // 注册广播 发送消息 registerReceiver(sendMessage, new IntentFilter(SENT_SMS_ACTION)); registerReceiver(receiver, new IntentFilter(DELIVERED_SMS_ACTION)); } public void unRegisterSmsReceiver(){ // 注销广播 发送消息 unregisterReceiver(sendMessage); unregisterReceiver(receiver); } private BroadcastReceiver sendMessage = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //判断短信是否发送成功 switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(context, "短信发送成功", Toast.LENGTH_SHORT).show(); break; default: Toast.makeText(mContext, "发送失败", Toast.LENGTH_LONG).show(); break; } } }; private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //表示对方成功收到短信 Toast.makeText(mContext, "对方接收成功",Toast.LENGTH_LONG).show(); } }; /** * 参数说明 * destinationAddress:收信人的手机号码 * scAddress:发信人的手机号码 * text:发送信息的内容 * sentIntent:发送是否成功的回执,用于监听短信是否发送成功。 * DeliveryIntent:接收是否成功的回执,用于监听短信对方是否接收成功。 */ private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- // 获得短信管理器 SmsManager sms = SmsManager.getDefault(); // create the sentIntent parameter // 监控短信是否发送成功的Action和PendingIntent Intent sentIntent = new Intent(SENT_SMS_ACTION); PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent, 0); // create the deilverIntent parameter // 监控对方是否收到短信的Action和PendingIntent Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); PendingIntent deliverPI = PendingIntent.getBroadcast(this, 0, deliverIntent, 0); //如果短信内容超过70个字符 将这条短信拆成多条短信发送出去 if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, sentPI, deliverPI); } } else { sms.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI); } } //发送指定的短信,并保存 public void saveSms(String saveNumber, String saveText){ /** 拿到输入的手机号码 **/ String number = saveNumber; /** 拿到输入的短信内容 **/ String text = saveText; /** 手机号码 与输入内容 必需不为空 **/ if (!TextUtils.isEmpty(number) && !TextUtils.isEmpty(text)) { //发送短信 sendSMS(number, text); /**将发送的短信插入数据库**/ ContentValues values = new ContentValues(); //发送时间 values.put("date", System.currentTimeMillis()); //阅读状态 values.put("read", 0); //1为收 2为发 values.put("type", 2); //送达号码 values.put("address", number); //送达内容 values.put("body", text); //插入短信库 getContentResolver().insert(Uri.parse("content://sms"),values); } } ///////////////////////////////////////////////////////////////////// //判断短信是否发送成功,完整的状态 case Activity.RESULT_OK: message = "Message sent!"; error = false; break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: message = "Error."; break; case SmsManager.RESULT_ERROR_NO_SERVICE: message = "Error: No service."; break; case SmsManager.RESULT_ERROR_NULL_PDU: message = "Error: Null PDU."; break; case SmsManager.RESULT_ERROR_RADIO_OFF: message = "Error: Radio off."; break; //另一个将短信写入发件箱的方法 ContentValues cv = new ContentValues(); cv.put("address", destinationAddress); cv.put("person", ""); cv.put("protocol", "0"); cv.put("read", "1"); cv.put("status", "-1"); cv.put("body", text); this.getContentResolver().insert(Uri.parse("content://sms/sent"), cv); Log.e("sms", "短信已经保存"); SMS不能直接访问数据库,只能通过协议来访问数据,相关的协议: content://sms/inbox 收件箱 content://sms/sent 已发送 content://sms/draft 草稿 content://sms/outbox 发件箱 content://sms/failed 发送失败 content://sms/queued 待发送列表 在模拟器上Outbox可能没有数据。 数据库中sms相关的字段如下: _id 一个自增字段,从1开始 thread_id 序号,同一发信人的id相同 address 发件人手机号码 person 联系人列表里的序号,陌生人为null(不一定有联系人就有值) date 发件日期 protocol 协议,分为: 0 SMS_RPOTO, 1 MMS_PROTO read 是否阅读 0未读, 1已读 status 状态 -1接收,0 complete, 64 pending, 128 failed type ALL = 0; INBOX = 1; SENT = 2; DRAFT = 3; OUTBOX = 4; FAILED = 5; QUEUED = 6; body 短信内容 service_center 短信服务中心号码编号 subject 短信的主题 reply_path_present TP-Reply-Path locked 是否加锁 ///////////////////////////////////////////////////////////////////// android的源代码,sms支持的协议有: sURLMatcher.addURI("sms", null, SMS_ALL); sURLMatcher.addURI("sms", "#", SMS_ALL_ID); sURLMatcher.addURI("sms", "inbox", SMS_INBOX); sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID); sURLMatcher.addURI("sms", "sent", SMS_SENT); sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID); sURLMatcher.addURI("sms", "draft", SMS_DRAFT); sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID); sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX); sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID); sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED); sURLMatcher.addURI("sms", "failed", SMS_FAILED); sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID); sURLMatcher.addURI("sms", "queued", SMS_QUEUED); sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS); sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID); sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE); sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT); sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID); sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID); sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID); sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID); sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING); sURLMatcher.addURI("sms", "sim", SMS_ALL_SIM); sURLMatcher.addURI("sms", "sim/#", SMS_SIM); ///////////////////////////////////////////////////////////////////// 其中,delete方法中支持的协议为: SMS_ALL 根据参数中的条件删除sms表数据 SMS_ALL_ID 根据_id删除sms表数据 SMS_CONVERSATIONS_ID 根据thread_id删除sms表数据,可以带其它条件 SMS_RAW_MESSAGE 根据参数中的条件删除 raw表 SMS_STATUS_PENDING 根据参数中的条件删除 sr_pending表 SMS_SIM 从Sim卡上删除数据 //删除thread_id="3", _id="5"的数据 //SMS_CONVERSATIONS_ID:"content://sms/conversations/3" this.getContentResolver().delete(Uri.parse("content://sms/conversations/3"), "_id=?", new String[]{"5"}); 在数据库中每个发送者的thread_id虽然一样,但不是固定的,如果把一个发送者的全部数据删除掉, 然后换一个新号码发送短信时,thread_id是以数据库中最大的id+1赋值的。 ///////////////////////////////////////////////////////////////////// update支持的协议有很多: SMS_RAW_MESSAGE SMS_STATUS_PENDING SMS_ALL SMS_FAILED SMS_QUEUED SMS_INBOX SMS_SENT SMS_DRAFT SMS_OUTBOX SMS_CONVERSATIONS SMS_ALL_ID SMS_INBOX_ID SMS_FAILED_ID SMS_SENT_ID SMS_DRAFT_ID SMS_OUTBOX_ID SMS_CONVERSATIONS_ID SMS_STATUS_ID SMS_INBOX_ID测试: ContentValues cv = new ContentValues(); cv.put("thread_id", "2"); cv.put("address", "00000"); cv.put("person", "11"); cv.put("date", "11111111"); this.getContentResolver().update(Uri.parse("content://sms/inbox/4"), cv, null, null); 可以更新thread_id(但有可能混乱) ///////////////////////////////////////////////////////////////////// insert支持的协议: SMS_ALL SMS_INBOX SMS_FAILED SMS_QUEUED SMS_SENT SMS_DRAFT SMS_OUTBOX SMS_RAW_MESSAGE SMS_STATUS_PENDING SMS_ATTACHMENT SMS_NEW_THREAD_ID 向sms表插入数据时,type是根据协议来自动设置, 如果传入的数据中没有设置date时,自动设置为当前系统时间;非SMS_INBOX协议时,read标志设置为1 SMS_INBOX协议时,系统会自动查询并设置PERSON threadId为null或者0时,系统也会自动设置 一直为造不了"发送失败"的邮件而发愁,现在来做一个: content://sms/failed ContentValues cv = new ContentValues(); cv.put("_id", "99"); cv.put("thread_id", "0"); cv.put("address", "9999"); cv.put("person", "888"); cv.put("date", "9999"); cv.put("protocol", "0"); cv.put("read", "1"); cv.put("status", "-1"); //cv.put("type", "0"); cv.put("body", "短信内容"); this.getContentResolver().insert(Uri.parse("content://sms/failed"), cv); //插入短信是否设置了检验? type ALL = 0; INBOX = 1; SENT = 2; DRAFT = 3; OUTBOX = 4; FAILED = 5; QUEUED = 6; type是否被设置成了5? /////////////////////////////////////////////////////////////////////////////////// 希望读取彩信的同学,请参考这里 尊重别人的劳动:http://www.cnblogs.com/lycoris/archive/2011/04/27/2030714.html 代码主要实现一个读取彩信列表的功能,原本功能是点击某条彩信记录,显示相关包括文本、图片(该处并没有对音频附件处理)等,该处就把他们整合到一起了。 public class SmsPage extends ListActivity{ private final String TAG="SmsPage"; private final Uri CONTENT_URI = Uri.parse("content://mms/inbox"); //查询彩信收件箱 private final Uri CONTENT_URI_PART = Uri.parse("content://mms/part"); //彩信附件表 public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.smslist); Cursor cursor = getContentResolver().query(CONTENT_URI, null, null,null, null); if(cursor.getCount()>0){ MyAdapter adapter = new MyAdapter(this,R.layout.smsitem,cursor,new String[]{},new int[]{}); setListAdapter(adapter); } } public class MyAdapter extends SimpleCursorAdapter{ private String name=""; public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to); } @Override public void bindView(View view, Context context, Cursor cursor) { TextView address = (TextView)view.findViewById(R.id.sms_address); TextView body = (TextView)view.findViewById(R.id.sms_body); TextView date = (TextView)view.findViewById(R.id.sms_date); TextView sub = (TextView)view.findViewById(R.id.sms_sub); ImageView image = (ImageView)view.findViewById(R.id.sms_image); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date time=new Date(cursor.getLong(cursor.getColumnIndex("date"))*1000);//彩信时间 int id = cursor.getInt(cursor.getColumnIndex("_id"));//彩信Id String subject = cursor.getString(cursor.getColumnIndex("sub"));//彩信主题 Cursor cAdd =null; Cursor cPhones=null; Cursor cPeople=null; Cursor cPart=null; try { //根据彩信id从addr表中查出发送人电话号码,其中外键msg_id映射pdu表的id; String selectionAdd = new String("msg_id=" + id + "); Uri uriAddr = Uri.parse("content://mms/" + id + "/addr"); cAdd = getContentResolver().query(uriAddr, null, selectionAdd, null, null); //根据addr表中的电话号码在phones表中查出PERSON_ID,外键PERSON_ID映射people表中的_id if(cAdd.moveToFirst()){//该处会得到2条记录,第一条记录为发件人号码,第二条为本机号码 String number= cAdd.getString(cAdd.getColumnIndex("address")); cPhones = getContentResolver().query(Contacts.Phones.CONTENT_URI, new String[]{Contacts.Phones.PERSON_ID},Contacts.Phones.NUMBER +"=?",new String[]{number}, null); if(cPhones.getCount()>0){//根据phones表中的PERSON_ID查出 people 表中联系人的名字 while (cPhones.moveToNext()) { String pId = cPhones.getString(cPhones.getColumnIndex(Contacts.Phones.PERSON_ID)); Uri uriPeo = Uri.parse(Contacts.People.CONTENT_URI+"/"+pId); cPeople = getContentResolver().query(uriPeo, null,null,null, null); if(cPeople.getCount()>0){ String str=""; while (cPeople.moveToNext()) { if(str == ""){ str = cPeople.getString(cPeople.getColumnIndex(Contacts.People.DISPLAY_NAME)); }else{ str += ","+cPeople.getString(cPeople.getColumnIndex(Contacts.People.DISPLAY_NAME)); } } name=number+"/"+str;//如果通讯录中存在,则以 ‘电话号码/名字’ 形式显示 }else{ name=number;//如果是陌生人直接显示电话号码 } } }else{ name=number;//如果是陌生人直接显示电话号码 } } //根据彩信ID查询彩信的附件 String selectionPart = new String("mid="+id);//part表中的mid外键为pdu表中的_id cPart = getContentResolver().query(CONTENT_URI_PART, null,selectionPart,null, null); String bodyStr=""; String[] coloumns = null; String[] values = null; while(cPart.moveToNext()){ coloumns = cPart.getColumnNames(); if(values == null) values = new String[coloumns.length]; for(int i=0; i< cPart.getColumnCount(); i++){ values[i] = cPart.getString(i); } if(values[3].equals("image/jpeg") || values[3].equals("image/bmp") || values[3].equals("image/gif") || values[3].equals("image/jpg") || values[3].equals("image/png")){ //判断附件类型 image.setImageBitmap(getMmsImage(values[0]);//该处只会显示一张图片,如果有需求的朋友可以根据自己的需求将ImageView换成Gallery,修改一下方法 image.setVisibility(View.VISIBLE); }else if(values[3].equals("text/plain")){ /**该处详细描述一下 *发现一个版本问题,之前用的2.1版本的多台手机测试通过,结果用1.6的G2报异常 *经过调试发现,1.6版本part表中根本就没有"text"列,也就是values[13],所以就 *报错了,好像在1.6版本(我只测过G2,嘿嘿),就算是文本信息也是以一个附件形 *式存在_date里面也就是values[12]里面,与图片类似,但在2.1里面却不是这样存 *的,文本信息被存在了"text"这个字段中,且"_date"为null*/ if(values[12]!=null){//所以该处需判断一下,如果_date为null,可直接设置内容为"text" bodyStr=getMmsText(values[0]); }else{ bodyStr = values[13]; } } } if(!"".equals(subject) && subject != null){ try { sub.setText(new String(subject.getBytes("iso-8859-1"),"UTF-8"));//设置彩信主题的编码格式 } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } if(!"".equals(bodyStr)){ body.setText(bodyStr); } address.setText(name); date.setText(format.format(time)); } catch (RuntimeException e) { Log.e(TAG, e.getMessage()); }finally{ if(cAdd != null){ cAdd.close(); } if(cPart != null){ cPart.close(); } if(cPeople != null){ cPeople.close(); } if(cPhones != null){ cPhones.close(); } } super.bindView(view, context, cursor); } } private String getMmsText(String _id){ //读取文本附件 Uri partURI = Uri.parse("content://mms/part/" + _id ); InputStream is = null; StringBuilder sb = new StringBuilder(); try { is = getContentResolver().openInputStream(partURI); if(is!=null){ BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8")); String temp = reader.readLine(); while (temp != null) { sb.append(temp); temp=reader.readLine();//在网上看到很多把InputStream转成string的文章,没有这关键的一句,几乎千遍一律的复制粘贴,该处如果不加上这句的话是会内存溢出的 } } }catch (IOException e) { e.printStackTrace(); Log.v(TAG, "读取附件异常"+e.getMessage()); }finally{ if (is != null){ try { is.close(); }catch (IOException e){ Log.v(TAG, "读取附件异常"+e.getMessage()); } } } return sb.toString(); } private Bitmap getMmsImage(String _id){ //读取图片附件 Uri partURI = Uri.parse("content://mms/part/" + _id ); //ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream is = null; Bitmap bitmap=null; try { is = getContentResolver().openInputStream(partURI); //byte[] buffer = new byte[256]; //int len = -1; //while ((len = is.read(buffer)) != -1) { // baos.write(buffer, 0, len); //} //bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toByteArray().length); bitmap = BitmapFactory.decodeStream(is); }catch (IOException e) { e.printStackTrace(); Log.v(TAG, "读取图片异常"+e.getMessage()); }finally{ if (is != null){ try { is.close(); }catch (IOException e){ Log.v(TAG, "读取图片异常"+e.getMessage()); } } } return bitmap; } } /////////////////////////////////////////////////////////////////////////////////// //扩展参考 http://labs.ywlx.net/?p=899 http://www.cnblogs.com/not-code/archive/2011/12/01/2270903.html http://www.cnblogs.com/not-code/archive/2011/11/27/2265287.html /////////////////////////////////////////////////////////////////////////////////// |
|||
android的selector(背景选择器) | 背景选择器 | ||
android的selector(背景选择器) 关于listview,imageview和imagebutton,button等空间都要改变android原来控件的背景,网上资料不是很全,所以现在总结一下android的selector的用法。 首先android的selector是在drawable/xxx.xml中配置的。 先看一下listview中的状态: 把下面的XML文件保存成你自己命名的.xml文件(比如list_item_bg.xml),在系统使用时根据ListView中的列表项的状态来使用相应的背景图片。 drawable/list_item_bg.xml <?xml version="1.0" encoding="utf-8" ?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 默认时的背景图片 --> <item android:drawable="@drawable/pic1" /> <!-- 没有焦点时的背景图片 --> <item android:state_window_focused="false" android:drawable="@drawable/pic1" /> <!-- 非触摸模式下获得焦点并单击时的背景图片 --> <item android:state_focused="true" android:state_pressed="true" android:drawable= "@drawable/pic2" /> <!-- 触摸模式下单击时的背景图片 --> <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/pic3" /> <!--选中时的图片背景 --> <item android:state_selected="true" android:drawable="@drawable/pic4" /> <!--获得焦点时的图片背景 --> <item android:state_focused="true" android:drawable="@drawable/pic5" /> </selector> 使用些xml文件:第一种是在listview中配置android:listSelector="@drawable/list_item_bg 或者在listview的item中添加属性android:background=“@drawable/list_item_bg"即可实现,或者在java代码中使用:Drawable drawable = getResources().getDrawable(R.drawable.list_item_bg); ListView.setSelector(drawable);同样的效果。 但是这样会出现列表有时候为黑的情况,需要加上:android:cacheColorHint="@android:color/transparent" 使其透明。 其次再来看看Button的一些背景效果: android:state_selected是选中 android:state_focused是获得焦点 android:state_pressed是点击 android:state_enabled是设置是否响应事件,指所有事件 根据这些状态同样可以设置button的selector效果。也可以设置selector改变button中的文字状态。 以下就是配置button中的文字效果: drawable/button_font.xml <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:color="#FFF" /> <item android:state_focused="true" android:color="#FFF" /> <item android:state_pressed="true" android:color="#FFF" /> <item android:color="#000" /> </selector> Button还可以实现更复杂的效果,例如渐变啊等等。 drawable/button_color.xml <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> / <item android:state_pressed="true">//定义当button 处于pressed 状态时的形态。 <shape> <gradient android:startColor="#8600ff" /> <stroke android:width="2dp" android:color="#000000" /> <corners android:radius="5dp" /> <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp"/> </shape> </item> <item android:state_focused="true">//定义当button获得 focus时的形态 <shape> <gradient android:startColor="#eac100"/> <stroke android:width="2dp" android:color="#333333" color="#ffffff"/> <corners android:radius="8dp" /> <padding android:left="10dp" android:top="10dp" android:bottom="10dp" android:right="10dp"/> </shape> </item> </selector> 最后,需要在包含 button的xml文件里添加两项。假如是 main.xml 文件, 我们需要在<Button />里加两项。 android:focusable="true" android:backgroud="@drawable/button_color" 这样当你使用Button的时候就可以甩掉系统自带的那黄颜色的背景了,实现个性化的背景,配合应用的整体布局非常之有用啊。 |
|||
通话记录代码片段 | 安卓通话记录代码片段 | ||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Android操作联系人 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //需要注意的地方 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //简单处理日期的显示 SimpleDateFormat sfd = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); <!-- 读取联系人权限 --> <uses-permission android:name="android.permission.READ_CONTACTS"/> <!-- 拨打电话权限 --> <uses-permission android:name="android.permission.CALL_PHONE"/> //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //查看Android系统Java源代码的好地方 http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android-apps/ //我们关心的文件 com.android.providers.contacts.ContactsProvider2 //通过查看源代码,可以知道要按照电话号码获取某一个联系人可以使用: Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/113911111111"); //上面这个类我们关心的代码 //联系人相关的一些核心的源代码 static { // Contacts URI matching table final UriMatcher matcher = sUriMatcher; matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions", AGGREGATION_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/suggestions/*", AGGREGATION_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/photo", CONTACTS_PHOTO); matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter/*", CONTACTS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*", CONTACTS_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_LOOKUP_ID); matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_vcard/*", CONTACTS_AS_VCARD); matcher.addURI(ContactsContract.AUTHORITY, "contacts/as_multi_vcard/*", CONTACTS_AS_MULTI_VCARD); matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/", CONTACTS_STREQUENT); matcher.addURI(ContactsContract.AUTHORITY, "contacts/strequent/filter/*", CONTACTS_STREQUENT_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "contacts/group/*", CONTACTS_GROUP); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts", RAW_CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#", RAW_CONTACTS_ID); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/data", RAW_CONTACTS_DATA); matcher.addURI(ContactsContract.AUTHORITY, "raw_contacts/#/entity", RAW_CONTACT_ENTITY_ID); matcher.addURI(ContactsContract.AUTHORITY, "raw_contact_entities", RAW_CONTACT_ENTITIES); matcher.addURI(ContactsContract.AUTHORITY, "data", DATA); matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/#", PHONES_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter", PHONES_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/emails", EMAILS); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/#", EMAILS_ID); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/lookup/*", EMAILS_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter", EMAILS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/emails/filter/*", EMAILS_FILTER); matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS); matcher.addURI(ContactsContract.AUTHORITY, "data/postals/#", POSTALS_ID); matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS); matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID); matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY); matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE); matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH + "/#", SYNCSTATE_ID); matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP); matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions", AGGREGATION_EXCEPTIONS); matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*", AGGREGATION_EXCEPTION_ID); matcher.addURI(ContactsContract.AUTHORITY, "settings", SETTINGS); matcher.addURI(ContactsContract.AUTHORITY, "status_updates", STATUS_UPDATES); matcher.addURI(ContactsContract.AUTHORITY, "status_updates/#", STATUS_UPDATES_ID); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS); matcher.addURI(ContactsContract.AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SEARCH_SHORTCUT); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts", LIVE_FOLDERS_CONTACTS); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts/*", LIVE_FOLDERS_CONTACTS_GROUP_NAME); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/contacts_with_phones", LIVE_FOLDERS_CONTACTS_WITH_PHONES); matcher.addURI(ContactsContract.AUTHORITY, "live_folders/favorites", LIVE_FOLDERS_CONTACTS_FAVORITES); matcher.addURI(ContactsContract.AUTHORITY, "provider_status", PROVIDER_STATUS); } 1. 首先找到目标联系人的_ID,例如要找第一个联系人的_ID Cursor target = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null); target.moveToFirst(); String contactId = cursor.getString(target.getColumnIndex(ContactsContract.Contacts._ID)); 2. 接着透过_ID,来找电话号码,电话号码可能不止有一个,所以先判断有无电话号码 String IsPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); 3. 如果有电话号码,接着再去query电话号码 if( (Integer.parseInt(IsPhone) > 0) ) { Cursor phoneNumber = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null); while (phones.moveToNext()) { String strPhoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); } } //如果要查询e-mail 代码如下 Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,null,ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,null, null); while (emails.moveToNext()) { String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)); } 另外,相对完整的例子 Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null); while (cursor.moveToNext()) { String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); String hasPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if (Boolean.parseBoolean(hasPhone)) { Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null); while (phones.moveToNext()) { String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); } phones.close(); } Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,null,ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,null, null); while (emails.moveToNext()) { String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.CommonDataColumns.DATA)); } emails.close(); } cursor.close(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //一种通过电话号码获得联系人得方法 String[] projection = { ContactsContract.PhoneLookup.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; Log.d(TAG, "getPeople ---------"); // 将自己添加到 msPeers 中 Cursor cursor = this.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, // Which columns to return. ContactsContract.CommonDataKinds.Phone.NUMBER + " = '" + mNumber + "'", // WHERE clause. null, // WHERE clause value substitution null); // Sort order. if( cursor == null ) { Log.d(TAG, "getPeople null"); return; } Log.d(TAG, "getPeople cursor.getCount() = " + cursor.getCount()); for( int i = 0; i < cursor.getCount(); i++ ) { cursor.moveToPosition(i); // 取得联系人名字 int nameFieldColumnIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME); String name = cursor.getString(nameFieldColumnIndex); Log.i("Contacts", "" + name + " .... " + nameFieldColumnIndex); // 这里提示 force close m_TextView.setText("联系人姓名:" + name); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //再来一种通过电话号码获得联系人的方法 通过电话号码获取联系人 1. 系统内部提供了根据电话号码获取data表数据的功能,路径为:data/phones/filter/* 2. 用电话号码替换“*”部分就可以查到所需数据,获取“display_name”可以获取到联系人显示名 示例: //根据电话号码查询联系人名称 public void getContactsName(String phoneNumber) { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/"+phoneNumber); Cursor c = resolver.query(uri, new String[] { "display_name" }, null, null, null); while (c.moveToNext()) { System.out.println(c.getString(0)); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /**得到手机通讯录联系人信息**/ private void getPhoneContacts() { ContentResolver resolver = mContext.getContentResolver(); // 获取手机联系人 Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,PHONES_PROJECTION, null, null, null); if (phoneCursor != null) { while (phoneCursor.moveToNext()) { //得到手机号码 String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX); //当手机号码为空的或者为空字段 跳过当前循环 if (TextUtils.isEmpty(phoneNumber)) continue; //得到联系人名称 String contactName = phoneCursor.getString(PHONES_DISPLAY_NAME_INDEX); //得到联系人ID Long contactid = phoneCursor.getLong(PHONES_CONTACT_ID_INDEX); //得到联系人头像ID Long photoid = phoneCursor.getLong(PHONES_PHOTO_ID_INDEX); //得到联系人头像Bitamp Bitmap contactPhoto = null; //photoid 大于0 表示联系人有头像 如果没有给此人设置头像则给他一个默认的 if(photoid > 0 ) { Uri uri =ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,contactid); InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(resolver, uri); contactPhoto = BitmapFactory.decodeStream(input); }else { contactPhoto = BitmapFactory.decodeResource(getResources(), R.drawable.contact_photo); } mContactsName.add(contactName); mContactsNumber.add(phoneNumber); mContactsPhonto.add(contactPhoto); } phoneCursor.close(); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /**得到手机SIM卡联系人人信息**/ private void getSIMContacts() { ContentResolver resolver = mContext.getContentResolver(); // 获取Sims卡联系人 Uri uri = Uri.parse("content://icc/adn"); Cursor phoneCursor = resolver.query(uri, PHONES_PROJECTION, null, null, null); if (phoneCursor != null) { while (phoneCursor.moveToNext()) { // 得到手机号码 String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX); // 当手机号码为空的或者为空字段 跳过当前循环 if (TextUtils.isEmpty(phoneNumber)) continue; // 得到联系人名称 String contactName = phoneCursor .getString(PHONES_DISPLAY_NAME_INDEX); //Sim卡中没有联系人头像 mContactsName.add(contactName); mContactsNumber.add(phoneNumber); } phoneCursor.close(); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Android系统中的联系人也是通过ContentProvider来对外提供数据的,我们这里实现获取所有联系人、通过电话号码获取联系人、添加联系人、使用事务添加联系人。 获取所有联系人 1. Android系统中的联系人也是通过ContentProvider来对外提供数据的 2. 数据库路径为:/data/data/com.android.providers.contacts/database/contacts2.db 3. 我们需要关注的有3张表 raw_contacts:其中保存了联系人id data:和raw_contacts是多对一的关系,保存了联系人的各项数据 mimetypes:为数据类型 4. Provider的authorites为com.android.contacts 5. 查询raw_contacts表的路径为:contacts 6. 查询data表的路径为:contacts/#/data 这个路径为连接查询,要查询“mimetype”字段可以根据“mimetype_id”查询到mimetypes表中的数据 7. 先查询raw_contacts得到每个联系人的id,在使用id从data表中查询对应数据,根据mimetype分类数据 示例: //查询所有联系人 public void testGetAll() { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.android.contacts/contacts"); Cursor idCursor = resolver.query(uri, new String[] { "_id" }, null, null, null); while (idCursor.moveToNext()) { //获取到raw_contacts表中的id int id = idCursor.getInt(0); //根据获取到的ID查询data表中的数据 uri = Uri.parse("content://com.android.contacts/contacts/" + id + "/data"); Cursor dataCursor = resolver.query(uri, new String[] { "data1", "mimetype" }, null, null, null); StringBuilder sb = new StringBuilder(); sb.append("id=" + id); //查询联系人表中的 while (dataCursor.moveToNext()) { String data = dataCursor.getString(0); String type = dataCursor.getString(1); if ("vnd.android.cursor.item/name".equals(type)) sb.append(", name=" + data); else if ("vnd.android.cursor.item/phone_v2".equals(type)) sb.append(", phone=" + data); else if ("vnd.android.cursor.item/email_v2".equals(type)) sb.append(", email=" + data); } System.out.println(sb); } } 添加联系人 1. 先向raw_contacts表插入id,路径为:raw_contacts 2. 得到id之后再向data表插入数据,路径为:data 示例: //添加联系人 ublic void testInsert() { ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.android.contacts/raw_contacts"); ContentValues values = new ContentValues(); // 向raw_contacts插入一条除了ID之外, 其他全部为NULL的记录, ID是自动生成的 long id = ContentUris.parseId(resolver.insert(uri, values)); //添加联系人姓名 uri = Uri.parse("content://com.android.contacts/data"); values.put("raw_contact_id", id); values.put("data2", "FHM"); values.put("mimetype", "vnd.android.cursor.item/name"); resolver.insert(uri, values); //添加联系人电话 values.clear(); // 清空上次的数据 values.put("raw_contact_id", id); values.put("data1", "18600000000"); values.put("data2", "2"); values.put("mimetype", "vnd.android.cursor.item/phone_v2"); resolver.insert(uri, values); //添加联系人邮箱 values.clear(); values.put("raw_contact_id", id); values.put("data1", "zxx@itcast.cn"); values.put("data2", "1"); values.put("mimetype", "vnd.android.cursor.item/email_v2"); resolver.insert(uri, values); 使用事务添加联系人 1. 在添加联系人得时候是分多次访问Provider,如果在过程中出现异常,会出现数据不完整的情况,这些操作应该放在一次事务中 2. 使用ContentResolver的applyBatch(String authority,ArrayList<ContentProviderOperation> operations) 方法可以将多个操作在一个事务中执行 3. 文档位置: file:///F:/android-sdk-windows/docs/reference/android/provider/ContactsContract.RawContacts.html 示例: //使用事务添加联系人 public void testInsertBatch() throws Exception { ContentResolver resolver = getContext().getContentResolver(); ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); ContentProviderOperation operation1 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/raw_contacts")) // .withValue("_id", null) // .build(); operations.add(operation1); ContentProviderOperation operation2 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data2", "ZZH") // .withValue("mimetype", "vnd.android.cursor.item/name") // .build(); operations.add(operation2); ContentProviderOperation operation3 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data1", "18612312312") // .withValue("data2", "2") // .withValue("mimetype", "vnd.android.cursor.item/phone_v2") // .build(); operations.add(operation3); ContentProviderOperation operation4 = ContentProviderOperation // .newInsert(Uri.parse("content://com.android.contacts/data")) // .withValueBackReference("raw_contact_id", 0) // .withValue("data1", "zq@itcast.cn") // .withValue("data2", "2") // .withValue("mimetype", "vnd.android.cursor.item/email_v2") // .build(); operations.add(operation4); // 在事务中对多个操作批量执行 resolver.applyBatch("com.android.contacts", operations); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //判断电话号码是否重复的正则表达式 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String strMaxTelRex = "((\\d{11})|(^((\\+86)|(86))?(1(3|5|8|4[57])\\d{8,9})$)|^((\\d{7,8})|(\\d{4}|\\d{3})(\\d{7,8})$))"; // 判断是国内还是国外-国际直接存 if (Pattern.matches(strMaxTelRex, strNumber)) { // 去掉+86 Pattern nowPattern = Pattern.compile("^((\\+86)|(86))"); Matcher nowMatcher = nowPattern.matcher(strNumber); strNumber = nowMatcher.replaceFirst(""); // 判断是否为手机 if (Pattern.matches("^0?(1(3|5|8|4[57])\\d{8,9})$", strNumber)) { strNumber = strNumber.substring(strNumber.length() - 11); } else if (Pattern.matches("^(400|800)\\d{7}$", strNumber)) { // 判断是否为特殊服务号码 } else if (Pattern.matches("^0?(\\d{7,12})$", strNumber)) { // 判断是否为0开头的座机,座机只判断后7为 strNumber = strNumber.substring(strNumber.length() - 7); } else if (strNumber == null || "".equals(strNumber) || "null".equals(strNumber)) { // 其他的直接存储 strNumber = "未知的电话号码"; } } /** * 去重 判断是否为8位:是8位以及小于8位全对比,判断 */ if (mapCallLog.get(strNumber) != null) { mapCallLog.get(strNumber).callLogCount += 1; continue; } //需要处理电话号码为空 callLogDataBean.callNumber = cursor.getString(cursor .getColumnIndex("number")); if(null == callLogDataBean.callNumber || "".equals(callLogDataBean.callNumber) || "null".equals(callLogDataBean.callNumber)){ callLogDataBean.callNumber="私人电话号码"; } callLogDataBean.callNormalizedNumber = cursor.getString(cursor .getColumnIndex("normalized_number")); //需要处理联系人名称为空 callLogDataBean.contactsDisplayName = cursor.getString(cursor .getColumnIndex("name")); if (null == callLogDataBean.contactsDisplayName || "".equals(callLogDataBean.contactsDisplayName) || "null".equals(callLogDataBean.contactsDisplayName)) { callLogDataBean.contactsDisplayName="未知联系人"; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 获取联系人信息 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ContentResolver resolver = context.getContentResolver(); ArrayList<ConDataBean> contacts = new ArrayList<ConDataBean>(); if (null != phoneCursor && phoneCursor.getCount() > 0) { ConDataBean contact = null; for (phoneCursor.moveToFirst(); !phoneCursor.isAfterLast(); phoneCursor .moveToNext()) { contact = new ConDataBean(); Long contactid = phoneCursor.getLong(phoneCursor .getColumnIndex(Contacts._ID)); // // 得到联系人名称 String contactName = phoneCursor.getString(phoneCursor .getColumnIndex(Contacts.DISPLAY_NAME)); contact.setDisplayname(contactName); ArrayList<String> allphone = getmorePhone(context, Long.toString(contactid)); // 得到联系人ID String defaultnumer = null; if (allphone.size() > 0) { defaultnumer = allphone.get(0); } String lookup = phoneCursor.getString(phoneCursor .getColumnIndex(Contacts.LOOKUP_KEY)); contact.setLookupKey(lookup); contact.setPhonenumber(defaultnumer); contact.setContactsid(contactid); // contact.setMail(getEmail(Long.toString(contactid), context)); contact.setAllnumber(allphone); // 得到联系人头像ID Long photoid = phoneCursor.getLong(phoneCursor .getColumnIndex(Contacts.PHOTO_ID)); if (photoid > 0) { // 得到联系人头像Bitamp contact.setContactphoto(getcontactphoto(resolver, contactid, photoid)); } contacts.add(contact); } phoneCursor.close(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 比较耗时的查询电话号码是否有对应的联系人 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //获得所有的联系人 Cursor cur = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); //循环遍历 if (cur.moveToFirst()) { int idColumn = cur.getColumnIndex(ContactsContract.Contacts._ID); int displayNameColumn = cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); do { //获得联系人的ID号 String contactId = cur.getString(idColumn); //获得联系人姓名 String disPlayName = cur.getString(displayNameColumn); //查看该联系人有多少个电话号码。如果没有这返回值为0 int phoneCount = cur.getInt(cur .getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if(phoneCount>0){ //获得联系人的电话号码 Cursor phones = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); if(phones.moveToFirst()){ do{ //遍历所有的电话号码 String phoneNumber= phones.getString(phones .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); System.out.println(phoneNumber); }while(phones.moveToNext()); } } } while (cur.moveToNext()); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MIMETYPE ,在data数据库里面它的值是整型,如果看一下官方文档的话,给它所赋的值像 ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME 之类的都字符串,那么这些字符串和data里面的整型是怎么对应的呢。 其实就是通过mimetypes表来对应的。里面的对应列主要就是9个 : 1|vnd.android.cursor.item/email_v2 2|vnd.android.cursor.item/im 3|vnd.android.cursor.item/postal-address_v2 4|vnd.android.cursor.item/photo 5|vnd.android.cursor.item/phone_v2 6|vnd.android.cursor.item/name 7|vnd.android.cursor.item/organization 8|vnd.android.cursor.item/nickname 9|vnd.android.cursor.item/group_membership |