记,基于Android开发类似于微博的东东时,值得记录的几个问题~

基于Android与云平台的多功能社交软件(毕业设计)       一直想把毕业设计开源出来,到了今天也是时候了。由于题目是老师指导而成的,鉴于表达对老师的敬意,也以老师给的题名作为本篇的题目。感谢项老师的指导,我的设计非常顺利,也拿到很满意的成绩。       本社交软件基于Android开发平台进行软件设计,以Android Studio、Android SDK作为软件开发工具,Git作为版本控件工具。以科大讯飞平台为语音识别及文本转换作为语音识别... 阅读详情

作为一个Java的使用者,在经历了Web到服务端开发的工作后,今年终于开始接触一些android开发方面的工作了。

新的挑战~~最近有一个需求是在应用里开发一个类似于微博的功能模块,说难不难,说易不易~~

作为一名Android上的菜鸟,在开发的过程里还是遇到不少问题的。当然,紧接着的就是一个个的想办法解决问题~~~~~

一直想把过程中遇到的,自己觉得几个比较有意义的问题,及其解决方法记录下来,但苦逼的是最近一直没有多的时间~~~

今天又到了一周一度的美好周末,阳光明媚,那干脆起个早,来写一写,一来也给自己加深下印象~~~

另外,如果您也是一个刚刚开始接触Android的菜鸟,希望能给您带去一点帮助。

而同时,如果您看到其中的某处应用不当,或者有更好的实现方式,更希望您能不吝指出,帮助我进步~


问题剖析:


开发类似于微博的这种功能,首先想到的,自然就是会用到ListView。那么,这其中会遇到的几个问题在什么地方呢?


1、首先,与普通的ListView定义不同,像微博这种东西,内容存在“不确定性”。这个不确定性是指什么呢?比如,有的微博内容里可能会带有图片,而有的则可能为纯文本;而在带有图片的微博中,图片的数目也是不确定的。所以说,对于界面的定义,自然就不能再仅仅依靠布局文件了。而需要借助代码在类文件中实现“动态加载控件”。


2、第二个问题,也是很常见的问题,就是在该种界面中,通常会包含大量的图片,例如用户头像,微博内容里的图片等等。这个时候自然就需要新开线程去处理从服务器下载图片,并更新界面的操作。也就是所谓的“图片的异步加载”工作。


3、与之伴随而来的,就是关于图片加载的另一个问题,界面里的图片很多。如果每次加载时,我们都要从服务器去下载,首先的问题就是加载的速度;其次这样的实现方式,对于网络资源的使用,只能说“抵制铺张浪费,从我做起”。那么,对应的,就需要实现“图片的缓存”。


4、最后一个想要记录的问题,是比较有意思的问题,也是过程中让我最蛋疼的问题。那就是Android对于ListView控件的“Recycler”机制,导致图片会出现显示错乱的问题。


针对于这些问题,从床上爬起来理一理思路,重写了一个Demo,大体效果如下:

       

接着,我们就按照开发这个玩意儿的步骤走一遍,然后看针对于上面提出的几点值得注意的问题,其解决之道是什么?


一、布局文件的定义

正如同建筑师们建造一幢精美的建筑,得先画出设计图纸一样。我们既然要开发一个我们自己的“微博”,那我们就先搞出“微博”界面的布局文件。


但针对于这一点并没有太多值得额外提到的地方,只需要按照自己想要的样式来定义自己的布局文件就行了。



二、类的定义

当我们已经有了“设计图”,接下来就是实际的“建筑工作”了。


首先,我们会定义一个继承于Activity的类来关联我们定义的布局文件。

接着,因为我们所定义的微博内容的界面中,使用了ListView控件。而ListView控件的具体内容,则需要由一个Adapter来提供。所以我们还需要定义一个Adpater类。

这时候,我们上面谈到的第一个问题就来了:“内容的不确定性”。基于存在有的微博可能为纯文本,有的带有图片;带有图片的微博中,有的仅仅只有一张图片,有的可能两张,也有可能更多的这种情况。

那么,针对于图片的显示,我们就应该在代码中进行动态的添加对应数目的“ImageView”。


所以,在我们定义的Adpater中的getView方法中,可能会存在类似于这样的代码:

		BlogInfo info = blogsDownLoad.get(position);
		if (convertView == null) {
			// init item view
			convertView = mInflater.inflate(R.layout.micro_blog_item, null);
			holder = initViewHolder(convertView);
			// 如果该条微博还带有图片
			if (info.getImages() != null && !info.getImages().equals("")) {
				String[] imageArray = info.getImages().split(";");
				// 动态加载图片显示控件
				ImageView imageView = new ImageView(context);
				imageView.setLayoutParams(new ViewGroup.LayoutParams(250, 250));
				holder.images_layout.addView(imageView);
				//.....
			}
			convertView.setTag(holder);
		}

现在,简单的来说,我们已经初步解决了关于“动态加载控件的”问题。

而当我们已经定义好了显示微博内容的Adpater之后。我们马上将要面临的就是上面谈到的下一个问题:“图片的异步加载”。

那么,首先我们需要明确的就是,为什么我们要对图片做异步加载?这是因为:

在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行。

默认的情况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程我们称之为Main线程。

当我们通过某个组件来启动另一个组件的时候,这个时候默认都是在同一个线程当中完成的。当然,我们可以自己来管理我们的Android应用的线程,我们可以根据我们自己的需要来给应用程序创建额外的线程。

也就是说,在Android中,对于“应用界面”的管理,都是在主线程当中完成的。所以,永远不要在主线程中做耗时的操作!

在我们这里所说的“微博”来讲,从服务器去下载图片到我们的客户端应用进行显示,这就是一个所谓的耗时操作。更何况,我们下载的图片的数量可能还很大。

那么,如果我们不对其进行“异步下载”的处理,会带来的影响就例如:

直到我们界面上所需要显示的所有图片下载完成之前,主线程一直都处于一个“阻塞”的状态。

而这反应在用户体验上,也就是应用一直处于顿卡状态,无法响应用户其它任何的新的操作。

更糟糕的是,当我们的整个现场如果阻塞时间超过5秒钟(官方是这样说的),这个时候就会出现 ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。这当然是糟糕透了的情况。


所以,我们自然会选择对“下载图片”的操作进行“异步实现”。这听上去很高大上的术语,其实原理很简单。

既然不要在主线程当中做耗时的操作,那我们要做的既然就是新开一个辅助线程,到服务器下载图片,当图片下载完成后,再通知主线程更新界面的显示。

Android提供了两种方式来解决线程直接的通信问题,一种是Handler机制,另一种就是AsyncTask机制。


我们这里选择使用AsyncTask机制,来实现所谓的“图片的异步加载”:

public class AsynImageLoader extends AsyncTask<String, Integer, Bitmap> {
	private String imageUrl;
	private ImageView imageView;

	public AsynImageLoader(ImageView imageView) {
		this.imageView = imageView;
	}

	@Override
	protected Bitmap doInBackground(String... params) {
		Bitmap bitmap = null;
		try {
			imageUrl = params[0];
			URL url = new URL(imageUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			if (conn.getResponseCode() == 200) {
				InputStream inputStream = conn.getInputStream();
				bitmap = BitmapFactory.decodeStream(inputStream);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return bitmap;
	}

	@Override
	protected void onPostExecute(Bitmap result) {
		super.onPostExecute(result);
		if (result != null) {
			// 通过 tag 来防止图片错位
			if (imageView.getTag() != null
					&& imageView.getTag().equals(imageUrl)) {
				imageView.setImageBitmap(result);
			}
		}
	}
}
这个类的思路很简单,在该类的构造函数中,我们获取两个参数:

一个是要进行异步加载的图片的URL,我们通过这个URL进行网络下载。

另一个则是在应用中,要将这张加载的图片显示到程序界面上的ImageView控件。

接着,我们在doInBackground方法中,下载这张图片。当图片下载完成后,onPostExecute收到通知,将下载到的图片加载到对应的控件上去。

也就完成了,我们所谓的“图片的异步加载”的工作。


此时,我们已经对图片添加了“异步加载”的处理方式。这很不错,但这显然还远远不够,因为我们还需要解决我们上面谈到的第三个问题:“浪费可耻”!

之所以这样讲,是因为,此时我们对于获取图片的方式仍然只有一种,就是“从网络下载获取”。这样做的结果就是,我们上次下载好的图片,丝毫不具备重用性。

例如:我们此次浏览了一些内容后,退出了应用;又或者我们在不断上下滑动,或刷新着屏幕,基于Android中ListView自身的特点,都需要一次次的去重复下载图片。

这时,我们要做的,就是添加“缓存机制”,当我们从网络中下载好图片之后,就将下载好的图片存放到缓存当中去,当下次需要使用到某张图片资源的时候,我们先到缓存中去查看是否存在,如果存在则直接获取,如果不存在,才到网络上去下载。

这样做的好处很明显,一直为用户节省了“网络资源”,另外也很大程度上的提高了获取资源的速度。这是显而易见的,你家里有一个储物室,当你需要一件物品,先看看家里的储物室里有没有,有则直接拿来使用,没有的话,再驱车去外面的商场购买;和每次一有需求,则开着车跑到老远的地方购买,这其中节约的时间,不言而喻。


废话不说,Android中对于图片的内存缓存,最常使用到的是LruCache。所以,我们进一步改进程序,将“缓存”与“异步”结合起来,所以我们的图片加载工具类,可能变成了下面这样:

@SuppressLint("NewApi")
public class AsyncImageLoader {
	// 图片缓存
	private LruCache<String, Bitmap> mMemoryCache;
	//
	private static AsyncImageLoader instance = null;

	private AsyncImageLoader() {
		// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
		// LruCache通过构造函数传入缓存值,以KB为单位。
		int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		// 使用最大可用内存值的1/8作为缓存的大小。
		int cacheSize = maxMemory / 8;
		mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(String key, Bitmap bitmap) {
				// 重写此方法来衡量每张图片的大小,默认返回图片数量。
				return bitmap.getByteCount() / 1024;
			}
		};
	}

	public static AsyncImageLoader getInstance() {
		if (instance == null) {
			instance = new AsyncImageLoader();
		}
		return instance;
	}

	private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		Log.v("jiaqi,jiaqi", "ggogo");
		if (getBitmapFromMemCache(key) == null) {
			mMemoryCache.put(key, bitmap);
		}
	}

	private Bitmap getBitmapFromMemCache(String key) {
		return mMemoryCache.get(key);
	}

	public void displayImage(String imgUrl, ImageView imageView) {

		final Bitmap bitmap = getBitmapFromMemCache(imgUrl);
		if (bitmap != null) {
			Log.v("内存有了", "直接获取");
			imageView.setImageBitmap(bitmap);
		} else {
			Log.v("内存没得", "去网上下");
			AsyncImageTask task = new AsyncImageTask(imageView);
			task.execute(imgUrl);
		}
	}

	//
	class AsyncImageTask extends AsyncTask<String, Integer, Bitmap> {
		private String imageUrl;
		private ImageView imageView;

		public AsyncImageTask(ImageView imageView) {
			this.imageView = imageView;
		}

		@Override
		protected Bitmap doInBackground(String... params) {
			Bitmap bitmap = null;
			try {
				imageUrl = params[0];
				URL url = new URL(imageUrl);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setConnectTimeout(5000);
				conn.setRequestMethod("GET");
				if (conn.getResponseCode() == 200) {
					InputStream inputStream = conn.getInputStream();
					bitmap = BitmapFactory.decodeStream(inputStream);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

			return bitmap;
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			if (result != null) {
				// 通过 tag 来防止图片错位
				if (imageView.getTag() != null
						&& imageView.getTag().equals(imageUrl)) {
					imageView.setImageBitmap(result);
				}

				addBitmapToMemoryCache(imageUrl, result);
			}
		}
	}
}
这个类的实现,正如我们上面所讲的一样,我们首先在内存中开辟一片区域作为图片资源的缓存,每次加载一张图片时,我们都先看看缓存中是否已经有这张图片了,如果没有,我们才会去通过网络进行下载。

当然,这里为了偷懒和仅仅出于一个说明作用,仅仅只是简单的使用了内存缓存。实际开发中,更为科学的来讲,你还可以选择使用“多级缓存”,例如你还可以在本地文件中开辟缓存,实现:首先到内存缓存中查找,如果没有,则到本地文件中查找,如果还没有,再到网络上去下载。这样,就更为合理了。


当然,要十分优秀的实现这样的需求,需要花费不少的精力。所以也可以选择使用一些图片加载框架,例如:Android-Universal-Image-Loader。这些优秀的框架已经帮你实现了各种关于图片处理的需求,你只需要导入一个第三方包,然后调用API就搞定了。



走到此时,对于这样一个类似微博的功能,我们已经实现的算是不错了。但最让人蛋疼问题,也就是上述的第4个也是最后一个问题,就出现了。

你可能会发现这样的情况,本来位于ListView第7行的用户的头像,莫名其妙显示为第1行的用户的头像。然后在你上下滑动屏幕,ListView进行刷新的过程中,你蛋疼的发现:“擦,全尼玛乱套了”。。

而针对于这样的问题,只要你耐心,上网多查查资料,就会初步得到一个解决方案,为显示头像的ImageView控件,添加一个Tag,这个tag的值通常就使用的是这个ImageView对应要显示的图片的URL。

我最开始,也是这样解决的。但问题虽然解决了,我其实还是不没有很明白造成这样的情况的原因。于是当这个问题解决之后,我发现了一个更操作的问题。

上面我们说过了,“微博”的内容存在“不确定性”。于是,我又发现了这样的情况,当我点击加载更多按钮,获取到新的微博信息,然后下拉屏幕的过程中,也许第七条微博是没有图片内容的,但它却莫名其妙的加载出了一个图片内容,而同时你会发现,这个图片内容实际上是前面第二条微博的。

好吧,我只能说,我凌乱了。。。于是继续查资料,功夫不负有心人,终于在一片博客里发现了这个现象发生的原因,也就是所谓的“recycler”机制。

具体说明,可以参照这篇博客:【Android】ListView中getView的原理与解决多轮重复调用的方法


其实看了这明白了这篇博客之后,就会知道:之所以出现这样的错误情况,是因为我们在getView方法中,选择使用了一个viewHolder来帮助我们对界面中的控件进行复用。在这种情况下,我们的getView方法的实现通常类似于这样:

	public View getView(int position, View convertView, ViewGroup parent) {
		// 根据Position分别获取容器当中存放的每条微博的详情
		if(convertView==null){
			convertView = mInflater.inflate(R.layout.micro_blog_item, null);
			holder = initViewHolder(convertView);
		}else{
			holder = (ViewHolder) convertView.getTag();
		}
		// 通过holder获取item项的各个组件,为其做特定的赋值工作
		return convertView;
		
	}

但是,如果我们不使用viewHolder,而是每次调用getView方法时,都选择使用最原始的类似于:imageView = (ImageView) convertView.findViewByID(....)这样的方式的话,其实是不会出现这样的问题的。

你可能会想,既然这样,我们还为什么要使用viewHolder来帮助实现呢?原因很简单,我们前面也说到了,是为了实现复用,从而提高效率。

因为正常情况下,一个ListView中的每个item,也就是每个列表项,它的控件构成,其实是一样的。所以,我们当然不要花费更多的劳力,每次getView时,都去资源里findViewByID一次。

所以,在这种情况下,使用viewHolder就能很好的帮助我们避免这一个问题。但是,因为在我们这里“内容存在不确定性”的特殊情况下,就导致了上面我们所说的蛋疼的问题。

要理解我这里说的东西,首先需要弄没明白上面提到的这边博客里讲到的"recycler"机制。当明白这个机制 之后,我们就能对上面我所说的类似的错误情况,分析出原因了。

例如,我们第一次进到微博界面时,从服务器下载了5条微博信息到客户端进行显示,这个时候当程序调用getView方法时,他会判断为此时每个Item都是空的,都需要重新获取,所以,它都会走“if(convertView == null)”中的内容,但可能当你加载更多之后,向下滑动屏幕,想要浏览第六条或者第七条微博时,出于“recycler”机制,他就会去复用之前的convertView,所以这个时候也许就恰巧复用到了被放入"recycler"当中的原本第一条微博内容的“convertview”,而走到"else"里的代码执行。于是这个时候,错误的图片显示情况就出现了。


但是现在,错误已经不可怕了,因为我们已经知道了错误出现的原因,知道了原因,我们就能针对其给出解决方案。既然图片显示错误是因为复用了item内容造成的,那么,我们就应该在其复用时,额外再做一次判断。

例如,我们的微博界面中,原本的第一条微博带有1张图片内容,当我滑动屏幕到显示第七条微博时,因为这个时候会复用到第一条微博的convertView,所以原本不含有图片内容的第七条微博也显示出了一张图片。这个时候,我们要做的就是,在 复用Convertview的时候,额外做一个判断,先获取第七条微博的内容信息,判断其是否带有图片,如果不带有,我们则应该将复用的这个convertView中,用于显示微博所带图片内容的这个imageview控件去掉。这个时候,就不存在混乱的显示情况了。


所以,经过修改后的adpater类变为了下面的样子:

package com.tsr.mymicroblog;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

import com.tsr.bean.BlogInfo;
import com.tsr.util.AsyncImageLoader;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MicroBlogAdapter extends BaseAdapter {
	private Context context;
	// 存放下载微博的容器
	private List<BlogInfo> blogsDownLoad;
	private LayoutInflater mInflater;
	private ViewHolder holder;

	public MicroBlogAdapter(Context context, List<BlogInfo> blogsDownLoad) {
		this.context = context;
		this.blogsDownLoad = blogsDownLoad;
		this.mInflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	@Override
	public int getCount() {
		return blogsDownLoad.size();
	}

	@Override
	public Object getItem(int arg0) {
		// TODO 自动生成的方法存根
		return null;
	}

	@Override
	public long getItemId(int arg0) {
		// TODO 自动生成的方法存根
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// 根据Position分别获取容器当中存放的每条微博的详情
		final BlogInfo info = blogsDownLoad.get(position);
		if (convertView == null) {
			// init item view
			convertView = mInflater.inflate(R.layout.micro_blog_item, null);
			holder = initViewHolder(convertView);
			// 如果该条微博还带有图片
			if (info.getImages() != null && !info.getImages().equals("")) {
				String[] imageArray = info.getImages().split(";");
				// 动态加载图片显示控件
				fillBlogImageDynamic(holder, imageArray);
			}
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
			// 清除ListView的ReCycle机制当中的ImageView,避免图片显示错乱的情况
			if (holder.blog_detail_image != null
					&& holder.blog_detail_image.size() != 0) {
				cleanOldBlogImages(holder);
			}
			// 显示新的图片内容
			if (info.getImages() != null && !info.getImages().equals("")) {
				// 添加该条微博对应图片数量的的ImageView
				String[] imageArray = info.getImages().split(";");
				fillBlogImageDynamic(holder, imageArray);
			}

		}

		holder.user_nickname.setText(info.getUsername());
		holder.publish_time.setText(info.getTime());
		holder.blog_content.setText(info.getBlogtext());
		holder.btn_review.setText(context.getString(R.string.blog_review) + "("
				+ info.getReviewcount() + ")");
		holder.btn_nice.setText(context.getString(R.string.blog_nice) + "("
				+ info.getDianzancount() + ")");

		String headImgURL = MicroBlogActivity.USER_HEAD[position];
		holder.user_head.setTag(headImgURL);
		AsyncImageLoader.getInstance().displayImage(headImgURL,
				holder.user_head);

		// 根据不同情况,动态的设置微博详情内的图片内容
		Set<String> keySet = holder.blog_detail_image.keySet();
		for (String key : keySet) {
			String imageName = key;
			ImageView imageView = holder.blog_detail_image.get(key);
			imageView.setTag(imageName);
			AsyncImageLoader.getInstance().displayImage(imageName, imageView);
		}

		// 点赞按钮
		holder.btn_nice.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// do something there...
			}
		});
		// 举报按钮
		holder.btn_report.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// do something there...
			}
		});

		return convertView;
	}

	static class ViewHolder {
		// 相关界面组件
		private ImageView user_head;
		private TextView user_nickname;
		private TextView publish_time;
		private TextView blog_content;
		private ImageView blog_pics;
		private Button btn_report;
		private Button btn_review;
		private Button btn_nice;
		private LinearLayout images_layout;
		private HashMap<String, ImageView> blog_detail_image = new HashMap<String, ImageView>();
	}

	public void addItem(BlogInfo blog) {

		blogsDownLoad.add(blog);
	}

	private ViewHolder initViewHolder(View convertView) {
		holder = new ViewHolder();
		holder.user_head = (ImageView) convertView
				.findViewById(R.id.img_wb_item_head);
		holder.user_nickname = (TextView) convertView
				.findViewById(R.id.txt_wb_item_uname);
		holder.publish_time = (TextView) convertView
				.findViewById(R.id.txt_wb_item_time);
		holder.blog_content = (TextView) convertView
				.findViewById(R.id.txt_wb_item_content);
		holder.btn_report = (Button) convertView.findViewById(R.id.btn_report);
		holder.btn_review = (Button) convertView.findViewById(R.id.btn_review);
		holder.btn_nice = (Button) convertView.findViewById(R.id.btn_nice);
		holder.blog_pics = (ImageView) convertView
				.findViewById(R.id.img_wb_item_content_pic);
		holder.images_layout = (LinearLayout) convertView
				.findViewById(R.id.blog_images);

		return holder;
	}

	private void fillBlogImageDynamic(ViewHolder holder, String[] imageArray) {
		for (int i = 0; i < imageArray.length; i++) {
			ImageView imageView = new ImageView(context);
			imageView.setLayoutParams(new ViewGroup.LayoutParams(250, 250));
			holder.images_layout.addView(imageView);
			holder.blog_detail_image.put(imageArray[i], imageView);
		}
	}

	private void cleanOldBlogImages(ViewHolder holder) {
		HashMap<String, ImageView> imageMap = holder.blog_detail_image;
		// 删除原来的ImageView
		if (imageMap != null && imageMap.size() > 0) {
			holder.images_layout.removeAllViews();
			imageMap = new HashMap<String, ImageView>();
		}
	}
}

 

到了这里,提到的几个问题也讲完了~~~~~




Android实现社交应用中的评论和回复功能 在Android的日常开发中,评论与回复功能是我们经常遇到的需求之一,其中评论与回复列表的展示一般在功能模块中占比较大。对于需求改动和迭代较频繁的公司来说,如何快速开发一个二级界面来适应我们的功能需求无疑优先级更高一些。首先我们来看看其他社交类app的评论与回复列表如何展示的: Twitter不用说了,全球知名社交平台,上亿用户量,他们的评... 阅读详情

相关推荐

Android实战第二篇——模仿社交软件探探(一)

这是我的第二篇实战,相比于第一篇的看视频模仿着实战,这一篇完全是自己的实战,不借助任何视频教程,看视频的实战是在抄代码,而这一篇是完全自己构思,自己去解决问题的,这次的实战是仿照一个社交软件探探,该篇是这部分的第一篇主要讲一讲抽屉菜单DrawerLayout 以下是我们的效果,这是在点击了 左上角的按钮后呈现出来的。 看了效果图后,我们来回到布局代码中来看看吧! <andro

进击的程序猿 6126

开发简单Android聊天软件(7)

我们完成过消息接收和加载,构建一个完整的聊天流程。但是我们只完成了一半,现在我们来讲关于聊天记录的另一部分,离线消息数据获取。

weixin_41812640的博客 1437

Android小项目——社交类app(低仿微信)

客户端源码。移动端Android应用开发,一款社交类app,叫做”聚友“。该项目详情可以进我主页查看。转到:https://blog.youkuaiyun.com/yy2017220302028/article/details/100568254

Android聊天程序开发

(1)聊天页面Activity代码如下,其中sendData()方法是发送信息的方法,它获取你输入的信息,放入信息列表中,并且调用getDataFromServer()方法,将你输入的信息发送到服务器,从服务器获取返回信息,从而实现和机器人的聊天功能。开发一款机器人聊天程序,首先来看效果,如图所示,它是一个简单的聊天界面,我问了它一个关于文学的问题和一个关于计算机程序的问题,看起来这两个问题回答得还不错,那么接下来就来看看这款程序是怎么开发的。方法用于发送聊天信息并获取服务器返回的聊天信息。

liuhaikang的博客 2078

保姆级教程:用R包ggpicrust2搞定PICRUSt2功能预测,从差异分析到热图PCA一键出图

本文提供了一份详细的ggpicrust2使用指南,帮助用户从零开始完成PICRUSt2功能预测数据的差异分析和可视化。通过安装配置、数据导入、差异分析到高级可视化(如热图和PCA)的全流程讲解,即使是初学者也能快速掌握发表级图表的生成技巧。

weixin_33712987的博客 383

STM32 ES8266上阿里云IOT MQTT实践【第七章】:ESP8266+MQTT上阿里云物联网平台实践

本篇文章讲述了如何使用stm32接入阿里云IOT,WiFi模块为ESP8266,可以实现发布以及订阅。

超级大洋葱的博客 2万+

android 中判断系统应用和第三方应用

方法一.获取全部应用: PackageManagerpackageManager=getPackageManager(); IntentmIntent=newIntent(Intent.ACTION_MAIN,null); mIntent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo>listAllApps=packageManager.queryInt...

liaobinliaobin的博客 2968

STM32H743IIT6+USB3300高速HID通讯实战:从CUBEMX配置到调试避坑全记录

本文详细记录了基于STM32H743IIT6微控制器与USB3300 PHY芯片实现高速USB HID通信的完整实战过程。内容涵盖从硬件选型、STM32CubeMX工程配置(重点规避引脚映射等常见陷阱)、自定义报告描述符编写,到驱动层数据收发实现与高级调试技巧的全链路解析,旨在帮助开发者快速构建稳定、高效的嵌入式高速数据通道。

vscode5coder的博客 362

android中遇到的问题怎么解决方法,Android问题收集——实际项目中遇到的问题及解决方案...

1、WebView闪烁问题场景:游戏详情,webview上下方都有控件,然后web加载内容后有一定几率不停闪烁原因分析:未知,可能是webview不能跟太多控件放一起。解决方案:用TextView显示html格式字符串http://www..com/mxgsa/archive/2012/11/15/2760256.html2、android.view.WindowManager$BadTokenE...

weixin_30393279的博客 796

Infineon MOSFET开关损耗计算实战指南

本文是英飞凌MOSFET开关损耗计算的实战指南。针对MOSFET发热问题,文章深入剖析了开关损耗的产生机理,并基于Infineon数据手册的关键参数,手把手推导了开通与关断损耗的工程计算公式。同时,指南强调了栅极电阻、寄生参数等实际因素对损耗的影响,并提供了从理论计算到实测优化的完整路径,帮助工程师精准评估与优化电源设计。

weixin_29212867的博客 441

GprMax正演模拟:为什么你的反射波‘消失’了?可能是直达波在捣鬼(附Python后处理脚本)

本文深入探讨了GprMax正演模拟中反射波信号被直达波淹没的现象,并提供了Python后处理脚本的实战指南。通过时域置零、自适应增益控制、频域滤波等多种技术,有效增强反射波信号,帮助用户解决探地雷达模拟中的常见问题。文章还包含详细的代码示例和效果评估方法,适合GPR数据处理的研究人员和工程师参考。

weixin_29305313的博客 232

APP社交类项目(类微信)设计之六ANDROID前台架构及通信介绍

前台安卓功能采用了MVP架构,与后台通信使用了当前主流的RetrofitManager网络通信插件,底层通信过程封装了Okhttp。在与后台实际通信过程中,RetrofitManager是单例模式,如下图所示 对于和后台通信的数据,统一进行了BEAN模型格式的数据转换,以便前台数据和后台数据进行适配。 下述前台声明的接口与后台API接口相互对应, ...

803

CAN轴控制流【6041,6040】

【代码】CAN轴控制流【6041,6040】

cfqq1989的博客 1782

Android怎么开发一个社交APP

00_课程介绍.txt 01_应用资源.zip 02_最终源码.zip 03_笔记.zip 视频: 01_项目简介.avi 02_项目功能演示.avi 03_环信SDK介绍.avi 04_环信官方demo演示.avi 05_应用程序分包.avi 06_集成EaseUI.avi ...

qq_38472089的博客 1102

AXI2APB_bridge 设计

此处研究AHB Slave到 APB master的桥接器,实现接口转化,例如下图之后是参数描述

Starry__的博客 4035

手把手教你学simulink(13.2)--构建一个单相并网逆变器Simulink仿真模型

基于Simulink的单相并网逆变器项目的详细仿真实例。这个项目将重点放在逆变器的详细建模、最大功率点跟踪(MPPT)控制策略、并网控制和仿真分析上。我们将使用MATLAB/Simulink来构建整个系统,并通过具体的代码和模块来实现各个功能。

MHD0815的博客 545

[Android分享] 社交软件开发解决方案

一个简单的社交软件解决方案应该解决的问题:通信/共享/空间(朋友圈)/用户/好友/动态/聊天记录.社交主要是交流、朋友圈(空间)。通信又分为文本、表情、语音、视频。朋友圈主要包含自己和好友动态,有些还包含系统推荐的信息。这是我使用Bmob提供的解决方案案例:通信:建立Bmob推送服务基础上,有消息需要将通知或者信息推送到参与者端,文本可以直接推送,对于视频,语音推送的是存储连...

weixin_33884611的博客 313

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

android 实现分享功能两种方法

当我想做一个智能的记事本的时候,我就在尝试自己写一组分享功能。后来才知道,原来每个社交软件中都有自己的分享接口。 这就大大减少了我们的代码量了。 第一种方法:特点--简单 package com.example.share; import android.app.Activity; import android.content.Intent; import andro...

dianying7959的博客 988

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

android 社交软件开发,基于Android的移动端社交软件的设计与实现

摘要:随着移动互联技术以及移动社交的快速发展,市场对移动互联社交软件的需求越来越强烈。为了满足日益增长的移动社交软件的需求的要求,提高软件的服务质量,设计开发一套移动社交软件很有必要。在这样的背景下,本文提出了基于Android系统移动社交软件的开发。社交软件在过去很长一段时间都是PC机占主导地位;然而随着移动设备的普及,移动端的手机软件的市场份额逐渐的增加。本文研究的移动社交软件是基于Andro...

weixin_35079944的博客 1819

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Android开发常见问题及解决方法

1.R.java消失或解析异常 查看res中资源文件,图片,xml等。比如图片文件名不能有大写不能有空格。 搞定错误之后Project->clean就可以了。 2.自定义title栏。 首先要z在values->styles中定义一个style,然后在mainfest文件中设置android:theme. 最后在Activity中按照这个顺序写:...

weixin_34032779的博客 551

打造自己的Android聊天软件(socket篇)

其实打造属于自己的聊天软件很简单,今天我们讲学习如何简单的写一个群组聊天app,通过sockets实现。这不是唯一的方法,但却是最快和最简单的。最好和最有效的方式应该是使用推送通知(push notifications )而不是sockets。 翻译自http://www.androidhive.info/2014/10/android-building-group-chat-app-using

请叫我搞向 4万+

js与android交互难点之 js唤起app

最近写项目,遇到一个比较有意思的需求,涉及到公司所有前端工作,H5,Android,IOS 需求是这样的, 公司运营推广APP,对外分享一个链接,这个链接由h5写成,主要就是用来介绍APP的,蛋疼的是,在末尾加了两个按钮,一个用来下载IOS的app,一个用来下载android的app,重点来了啊,在点击按钮之前先判断手机中是否有该APP,有的话,直接打开,没有的话,就直接下载! 听着需求挺简...

weixin_40430041的博客 3218

APP社交类项目(类微信)设计之一整体规划

目标:社交类APP 功能:实现注册,登录,好友,单聊,群聊,聊天室,任务奖励,个人设置,支付 团队配置:ANDROID两人,UI一人,后台JAVA一人(项目经理),其他一人(产品经理+全局管理+融资) 技术选型: 后台----接口模块:SSM,REDIS,SWAGGER-UI, 管理模块:SpringBoot,beetl,shiro ...

4092
上一篇: 理解Java回调机制
下一篇: Android - SurfaceView + MediaPlayer实现分段视频无缝播放
LaterEqualsNever
博客等级 码龄12年 297粉丝 61原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值