精华内容
下载资源
问答
  • 前面的一篇文章给大家关于时间知识点的分享http://blog.csdn.net/wwj_748/article/details/11536421,本篇博客笔者想给大家分享的事如何通过Calendar来计算我们平时需要用到的年、月、日、时、分、秒等,还有如何...

    转自:IT_xiao小巫,http://blog.csdn.net/wwj_748

    前面的一篇文章给大家关于时间知识点的分享http://blog.csdn.net/wwj_748/article/details/11536421,本篇博客笔者想给大家分享的事如何通过Calendar来计算我们平时需要用到的年、月、日、时、分、秒等,还有如何计算类似朋友圈动态发布的时间。

    这里要大家去了解的几个重要的类:
    日历类:Calendar
    日期类:Date
    日期格式类:SimpleDateFormat
    基本上我们对日期的一些转换都是在这几个类中进行的,有以下几个常用的转换:
    通过Calendar获取相关日期信息,比如当前的年月日,去年的年月日、下一年的年月日等,这里可以查看笔者关于自定义日历的一篇博文,有提供相关的日期工作类:http://blog.csdn.net/wwj_748/article/details/42244865
    Date与Calendar之间的转换
    Date与日期字符串的互相转换

    首先我们来看如何使用Calendar来获取我们想要的日期信息:

    获取当前日历:
    Calendar calendar = Calendar.getInstance(); 
    根据字段来获取信息
    获取当前年份:
    Calendar.getInstance().get(Calendar.YEAR)
    获取当前月:

    Calendar.getInstance().get(Calendar.MONTH) + 1

    注意这里要+1,因为月份是从0开始的。
    获取当前月的第几天:

    calendar.get(Calendar.DAY_OF_MONTH)

    获取当前时间毫秒显示:

    calendar.getTimeInMillis() 


    Calendar还可以做很多一些运算,笔者在开发日历控件的时候,就做了很多关于日期的运算:
    通过Calendar的add方法来对指定字段进行运算,举例如下:
    // 获取去年的年份  
        public static int getLastYear() {  
            Calendar c = Calendar.getInstance();  
            c.add(Calendar.YEAR, -1);  
            return c.get(Calendar.YEAR);  
        }  
          
        // 获取下一年年份  
        public static int getNextYear() {  
            Calendar c = Calendar.getInstance();  
            c.add(Calendar.YEAR, 1);  
            return c.get(Calendar.YEAR);  
        }
    
        // 获取上一个月的月份  
            public static int getLastMonth() {  
                Calendar c = Calendar.getInstance();  
                c.add(Calendar.MONTH, -1);  
                return c.get(Calendar.MONTH) + 1;  
            }  
              
            // 获取下一个月的月份  
            public static int getNextMonth() {  
                Calendar c = Calendar.getInstance();  
                c.add(Calendar.MONTH, 1);  
                return c.get(Calendar.MONTH) + 1;  
            }  

    关于Calendar还有很多字段,希望读者查看API自己去尝试。



    Date与Calendar之间的转换
    我们可以通过调用Calendar的setTime方法来设置时间所对应的日历对象
    calendar.setTime(new Date())


    Date与日期字符串的互相转换
    在实际开发中,我们经常需要对服务端返回的日期数据进行格式化,这里就需要用到SimleDateFormat,我们可以指定日期格式:
        private static SimpleDateFormat DATE_FORMAT_TILL_SECOND = new SimpleDateFormat(  
                "yyyy-MM-dd HH:mm:ss");  

    我们得到一个日期字符串,可以将其转换为Date对象:


    /** 
         * 日期字符串转换为Date 
         * @param dateStr 
         * @param format 
         * @return 
         */  
        public static Date strToDate(String dateStr, String format) {  
            Date date = null;  
      
            if (!TextUtils.isEmpty(dateStr)) {  
                DateFormat df = new SimpleDateFormat(format);  
                try {  
                    date = df.parse(dateStr);  
                } catch (ParseException e) {  
                    e.printStackTrace();  
                }  
            }  
            return date;  
        } 

    也可以将我们获取的Date对象转换为指定格式的字符串:

    /** 
         * 日期转换为字符串 
         * @param timeStr 
         * @param format 
         * @return 
         */  
        public static String dateToString(String timeStr, String format) {  
            // 判断是否是今年  
            Date date = strToDate(timeStr, format);  
            Calendar calendar = Calendar.getInstance();  
            calendar.setTime(date);  
            // 如果是今年的话,才去“xx月xx日”日期格式  
            if (calendar.get(Calendar.YEAR) == getYear()) {  
                return DATE_FORMAT_TILL_DAY_CURRENT_YEAR.format(date);  
            }  
              
            return DATE_FORMAT_TILL_DAY_CH.format(date);  
        }
    最后要说的就是,开发中我们需要计算一条动态的发布时间,一般我们得到的是一条日期字符串,我们需要计算当前时间和动态的发布时间,计算它们的差值来判断发布在什么时候,上代码:

        /** 
             * 日期逻辑 
             * @param dateStr 日期字符串 
             * @return 
             */  
            public static String timeLogic(String dateStr) {  
                Calendar calendar = Calendar.getInstance();  
                calendar.get(Calendar.DAY_OF_MONTH);  
                long now = calendar.getTimeInMillis();  
                Date date = strToDate(dateStr, DATE_FORMAT);  
                calendar.setTime(date);  
                long past = calendar.getTimeInMillis();  
                  
                // 相差的秒数  
                long time = (now - past) / 1000;  
                  
                StringBuffer sb = new StringBuffer();  
                if (time > 0 && time < 60) { // 1小时内  
                    return sb.append(time + "秒前").toString();  
                } else if (time > 60 && time < 3600) {  
                    return sb.append(time / 60+"分钟前").toString();  
                } else if (time >= 3600 && time < 3600 * 24) {  
                    return sb.append(time / 3600 +"小时前").toString();  
                }else if (time >= 3600 * 24 && time < 3600 * 48) {  
                    return sb.append("昨天").toString();  
                }else if (time >= 3600 * 48 && time < 3600 * 72) {  
                    return sb.append("前天").toString();  
                }else if (time >= 3600 * 72) {  
                    return dateToString(dateStr, DATE_FORMAT);  
                }  
                return dateToString(dateStr, DATE_FORMAT);  
            }  

    效果类似这种:

    本文的记录到这里,这里提一点,开发中我们经常遇到的一些知识点是可以整理和总结的,移动的产品开发是需要快递迭代的,对这些开发知识点的记录对自己有很大的帮助,提升你自己的开发效率,对应的你的能力就提升了,薪资也会跟着往上升,小巫个人的一点观点,感谢你的耐心阅读。

    展开全文
  • 实现朋友动态功能,支持动态加载图片,双击放大,手势缩放,滑动查看功能。
         转载请注明出处: http://blog.csdn.net/sk719887916/article/details/40348873 作者skay:
         最近参与了开发一款旅行APP,其中包含实时聊天和动态评论功能,终于耗时几个月几个伙伴完成了,今天就小结一下至于实时聊天功能如果用户不多的情况可以scoket实现,如果用户万级就可以采用开源的smack + opnefile实现,也可以用mina开源+XMMP,至于怎么搭建和实现,估计目前github上一搜一大把,至于即时通讯怕误人子弟,暂且不做介绍,现就把实现的一个微信朋友圈的小功能介绍一下。
       先上效果图:
     
    u
       一拿到主流的UI需求,大致分析下,需要我ListView嵌套Gridview,而gridView的行数也和图片总数有关系,因此通过个数我们可以动态设置条目的宽高,而点击图片放大我们可一跳转到另一界面,图片左右滑动可以用viewpager实现,双击图片放大和手指缩放图片也可以用就监听手势进行不断放大,对于安卓事件不熟悉的朋友可以直接使用一个著名的photoVIew开源项目,支持手势缩放图片和滑动图片实现画廊功能,也很好的解决了内存溢出问题。
    
    
          一 配置ImageLoader
           
           本项目中加载网络图片我就直接使用imageLoader,但建议还是去看下源码,因为开源项目本身自带缓存机制,有很好的缓存技巧,有很多东西值得我们借鉴。其不仅可以加载本地图片(文件path),也支持加载网络图片(url),并且自带防止内存溢出功能。
        
    public class MyApplication extends Application {
    
    	@Override
    	public void onCreate() {
    		super.onCreate();
    		DisplayImageOptions defaultOptions = new DisplayImageOptions
    				.Builder()
    				.showImageForEmptyUri(R.drawable.empty_photo) 
    				.showImageOnFail(R.drawable.empty_photo) 
    				.cacheInMemory(true)
    				.cacheOnDisc(true)
    				.build();
    		ImageLoaderConfiguration config = new ImageLoaderConfiguration
    				.Builder(getApplicationContext())
    				.defaultDisplayImageOptions(defaultOptions)
    				.discCacheSize(50 * 1024 * 1024)//
    				.discCacheFileCount(100)//缓存一百张图片
    				.writeDebugLogs()
    				.build();
    		ImageLoader.getInstance().init(config);
    	}
    }
     
     二 准备主界面和需要的基础类
      1  Listadapter 
    
    public class  FridListAdapter extends BaseAdapter{
    
    	private ArrayList<MyBean> mList;
    	private LayoutInflater mInflater;
    	private Context mContext;
    	
    	public FridListAdapter(Context context,ArrayList<MyBean> list) {
    		mInflater = LayoutInflater.from(context);
    		mContext=context;
    		this.mList=list;
    	}
    
    	@Override
    	public int getCount() {
    		return mList==null?0:mList.size();
    	}
    
    	@Override
    	public MyBean getItem(int position) {
    		return mList.get(position);
    	}
    
    	@Override
    	public long getItemId(int position) {
    		return getItem(position).id;
    	}
    
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		ViewHolder holder;
    		if (convertView == null) {
    			holder = new ViewHolder();
    			convertView = mInflater.inflate(R.layout.list_item, null);
    			holder.avator=(ImageView)convertView.findViewById(R.id.avator);
    			holder.name=(TextView)convertView.findViewById(R.id.name);
    			holder.content = (TextView) convertView.findViewById(R.id.content);
    			holder.gridView=(NoScrollGridView)convertView.findViewById(R.id.gridView);
    			convertView.setTag(holder);
    		} else {
    			holder = (ViewHolder) convertView.getTag();
    		}
    		final MyBean bean = getItem(position);
    		//加载网络图片
    		ImageLoader.getInstance().displayImage(bean.avator, holder.avator);
    		holder.name.setText(bean.name);
    		holder.content.setText(bean.content);
    		if(bean.urls!=null&&bean.urls.length>0){
    			holder.gridView.setVisibility(View.VISIBLE);
    			holder.gridView.setAdapter(new DynamicGridAdapter(bean.urls, mContext));
    			holder.gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    
    				@Override
    				public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    					imageBrower(position,bean.urls);
    				}
    			});
    		}else{
    			holder.gridView.setVisibility(View.GONE);
    		}
    		return convertView;
    	}
    
    	private void imageBrower(int position, String[] urls) {
    		Intent intent = new Intent(mContext, ImagePagerActivity.class);
    		// 图片url,为了演示这里使用常量,一般从数据库中或网络中获取
    		intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_URLS, urls);
    		intent.putExtra(ImagePagerActivity.EXTRA_IMAGE_INDEX, position);
    		mContext.startActivity(intent);
    	}
    	
    	// 优化listview
    	private static class ViewHolder {
    
    		public TextView name;
    		public ImageView avator;
    		TextView content;
    		NoScrollGridView gridView;
    	}
    }
    

    2 主界面
       实际项目中数据是数据是从服务器获取的,本次就只将图片从网络获取,
    public class MainActivity extends ListActivity {
    
    	public static final String TAG = "MainActivity";
    	private FridListAdapter mAdapter;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    
    		new LoderDataTask().execute();
    
    	}
    
    	class LoderDataTask extends AsyncTask<Void, Void, MessageModle> {
    
    		@Override
    		protected MessageModle doInBackground(Void... params) {
    
    			Gson gson = new Gson();
    
    			MessageModle msg = gson.fromJson(getData(), MessageModle.class);
    			return msg;
    		}
    
    		@Override
    		protected void onPostExecute(MessageModle result) {
    			mAdapter = new FridListAdapter(MainActivity.this, result.list);
    			setListAdapter(mAdapter);
    
    		}
    
    	}
    
    	private String getData() {
    		// 模拟网络获取数据
    		String json = "{\"code\":200,\"msg\":\"ok\",list:["
    				+ "{\"id\":110,\"avator\":\"http://img0.bdstatic.com/img/image/shouye/leimu/mingxing.jpg\",\"name\":\"赵薇\",\"content\":\"今天不开心!\",\"urls\":[]},"
    				+ "{\"id\":111,\"avator\":\"http://image.cnwest.com/attachement/jpg/site1/20110507/001372d8a36f0f2f4c953a.jpg\",\"name\":\"李晨\",\"content\":\"我们\","
    				+ "  \"urls\":[\"http://guangdong.sinaimg.cn/2015/0530/U11307P693DT20150530094310.jpg\"]},"
    
    				+ "{\"id\":114,\"avator\":\"http://img.hexun.com/2009-05-01/117287830.jpg\",\"name\":\"小马哥\",\"content\":\"今天淘宝了吗\",\"urls\":["
    				+ "\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=ccd33b46d53f8794d7ff4b26e2207fc9/0d338744ebf81a4c0f993437d62a6059242da6a1.jpg\","
    				
    				+ "\"http://f.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=6b62f61bac6eddc422e7b7f309e0c7c0/6159252dd42a2834510deef55ab5c9ea14cebfa1.jpg\","
    				+ "\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=e58fb67bc8ea15ce45eee301863b4bce/a5c27d1ed21b0ef4fd6140a0dcc451da80cb3e47.jpg\","
    				+ "\"http://c.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=cdab1512d000baa1be2c44b3772bc82f/91529822720e0cf3855c96050b46f21fbf09aaa1.jpg\"]},"
    				
    				+ "{\"id\":112,\"avator\":\"http://img3.yxlady.com/yl/UploadFiles_5361/20150528/20150528050208705.jpg\",\"name\":\"邓超\",\"content\":\"奔跑吧兄弟! 欢迎收看!\",\"urls\":[\"http://upload.cbg.cn/2015/0305/1425518659246.jpg\","
    				+ "\"http://www.people.com.cn/mediafile/pic/20150619/30/4179219540177204330.jpg\"]},"
    				
    				+ "{\"id\":113,\"avator\":\"http://img4.imgtn.bdimg.com/it/u=945108765,1070109457&fm=21&gp=0.jpg\",\"name\":\"奥巴马\",\"content\":\"holle\",\"urls\":[\"http://f.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=6b62f61bac6eddc422e7b7f309e0c7c0/6159252dd42a2834510deef55ab5c9ea14cebfa1.jpg\",\"http://g.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=e58fb67bc8ea15ce45eee301863b4bce/a5c27d1ed21b0ef4fd6140a0dcc451da80cb3e47.jpg\",\"http://c.hiphotos.bdimg.com/album/s%3D680%3Bq%3D90/sign=cdab1512d000baa1be2c44b3772bc82f/91529822720e0cf3855c96050b46f21fbf09aaa1.jpg\"]}]}";
    
    		return json;
    	}

    3 GridView的Adapter
         因为Listview的条目中包含Gridview,在这里还需要为它创建atapter
        由于adapter没太多技术含量,因此重点部分列出,在这里我们需要判断下适配的数据眼总数,微信最大数是9张,显示一张的时候,图片比较大,两张的时候稍微减少,四张的时候两列两行和两张的大小一致,其他张数的时候都是三行三列的九宫格。
    @Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		MyGridViewHolder viewHolder;
    		if (convertView == null) {
    			viewHolder = new MyGridViewHolder();
    			convertView = mLayoutInflater.inflate(R.layout.gridview_item,
    					parent, false);
    			
    			viewHolder.imageView = (ImageView) convertView
    					.findViewById(R.id.album_image);
    			convertView.setTag(viewHolder);
    		} else {
    			viewHolder = (MyGridViewHolder) convertView.getTag();
    		}
    		String url = getItem(position);
    		if (getCount() == 1) {
    			viewHolder.imageView.setLayoutParams(new android.widget.AbsListView.LayoutParams(300, 250));
    		}
    		
    		if (getCount() == 2 ||getCount() == 4) {
    			viewHolder.imageView.setLayoutParams(new android.widget.AbsListView.LayoutParams(200, 200));
    		}
    
    		ImageLoader.getInstance().displayImage(url, viewHolder.imageView);
    
    		return convertView;
    	}
    
     4 新建用于支持九宫格自定义的Gridview
      
    public class NoScrollGridView extends GridView {
    
    	public NoScrollGridView(Context context) {
    		super(context);
    	}
    
    	public NoScrollGridView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    	}
    	
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		int  expandSpec = 0;
    	    int  size = getAdapter().getCount();
    	    
    		if (size == 1) {
    			
    			setNumColumns(1);
    		} 
    		if ( size==2 || size == 4  ) {
    			setNumColumns(2);
    		}
    		else {
    			setNumColumns(3);
    		}
    		expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
    		super.onMeasure(widthMeasureSpec,expandSpec );
    	}
    
    }

     
    三 点击图片后的基础类
       1 建立大图查看器viewpaer
        
    public class ImagePagerActivity extends FragmentActivity {
    	private static final String STATE_POSITION = "STATE_POSITION";
    	public static final String EXTRA_IMAGE_INDEX = "image_index";
    	public static final String EXTRA_IMAGE_URLS = "image_urls";
    
    	private HackyViewPager mPager;
    	private int pagerPosition;
    	private TextView indicator;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.image_detail_pager);
    
    		pagerPosition = getIntent().getIntExtra(EXTRA_IMAGE_INDEX, 0);
    		String[] urls = getIntent().getStringArrayExtra(EXTRA_IMAGE_URLS);
    
    
    		mPager = (HackyViewPager) findViewById(R.id.pager);
    		ImagePagerAdapter mAdapter = new ImagePagerAdapter(
    				getSupportFragmentManager(), urls);
    		mPager.setAdapter(mAdapter);
    		indicator = (TextView) findViewById(R.id.indicator);
    
    		CharSequence text = getString(R.string.viewpager_indicator, 1, mPager
    				.getAdapter().getCount());
    		indicator.setText(text);
    		// 更新下标
    		mPager.setOnPageChangeListener(new OnPageChangeListener() {
    
    			@Override
    			public void onPageScrollStateChanged(int arg0) {
    			}
    
    			@Override
    			public void onPageScrolled(int arg0, float arg1, int arg2) {
    			}
    
    			@Override
    			public void onPageSelected(int arg0) {
    				CharSequence text = getString(R.string.viewpager_indicator,
    						arg0 + 1, mPager.getAdapter().getCount());
    				indicator.setText(text);
    			}
    
    		});
    		if (savedInstanceState != null) {
    			pagerPosition = savedInstanceState.getInt(STATE_POSITION);
    		}
    
    		mPager.setCurrentItem(pagerPosition);
    	}
    
    	@Override
    	public void onSaveInstanceState(Bundle outState) {
    		outState.putInt(STATE_POSITION, mPager.getCurrentItem());
    	}
    
    	private class ImagePagerAdapter extends FragmentStatePagerAdapter {
    
    		public String[] fileList;
    
    		public ImagePagerAdapter(FragmentManager fm, String[] fileList) {
    			super(fm);
    			this.fileList = fileList;
    		}
    
    		@Override
    		public int getCount() {
    			return fileList == null ? 0 : fileList.length;
    		}
    
    		@Override
    		public Fragment getItem(int position) {
    			String url = fileList[position];
    			return ImageDetailFragment.newInstance(url);
    		}
    
    	}
    2 查看大图界面
      
        转载请注明出处: http://blog.csdn.net/sk719887916/article/details/40348873 作者skay:
    public class ImageDetailFragment extends Fragment {
    	private String mImageUrl;
    	private ImageView mImageView;
    	private ProgressBar progressBar;
    	private PhotoViewAttacher mAttacher;
    
    	public static ImageDetailFragment newInstance(String imageUrl) {
    		final ImageDetailFragment f = new ImageDetailFragment();
    
    		final Bundle args = new Bundle();
    		args.putString("url", imageUrl);
    		f.setArguments(args);
    
    		return f;
    	}
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		mImageUrl = getArguments() != null ? getArguments().getString("url") : null;
    
    	}
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    		final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
    		mImageView = (ImageView) v.findViewById(R.id.image);
    		mAttacher = new PhotoViewAttacher(mImageView);
    		
    		mAttacher.setOnPhotoTapListener(new OnPhotoTapListener() {
    			
    			@Override
    			public void onPhotoTap(View arg0, float arg1, float arg2) {
    				getActivity().finish();
    			}
    		});
    		
    		progressBar = (ProgressBar) v.findViewById(R.id.loading);
    		return v;
    	}
    
    	@Override
    	public void onActivityCreated(Bundle savedInstanceState) {
    		super.onActivityCreated(savedInstanceState);
    		
    		
    		ImageLoader.getInstance().displayImage(mImageUrl, mImageView, new SimpleImageLoadingListener() {
    			@Override
    			public void onLoadingStarted(String imageUri, View view) {
    				progressBar.setVisibility(View.VISIBLE);
    			}
    
    			@Override
    			public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
    				String message = null;
    				switch (failReason.getType()) {
    				case IO_ERROR:
    					message = "下载错误";
    					break;
    				case DECODING_ERROR:
    					message = "图片无法显示";
    					break;
    				case NETWORK_DENIED:
    					message = "网络有问题,无法下载";
    					break;
    				case OUT_OF_MEMORY:
    					message = "图片太大无法显示";
    					break;
    				case UNKNOWN:
    					message = "未知的错误";
    					break;
    				}
    				Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
    				progressBar.setVisibility(View.GONE);
    			}
    
    			@Override
    			public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
    				progressBar.setVisibility(View.GONE);
    				mAttacher.update();
    			}
    		});
    		
    	}
    

         四  界面的头像圆形
          圆形头像用主流的circleimageview.jar的框架,但是有兴趣的朋友也可以自定义Imagview采用重写onDrawI()画圆形的方式将bitmap画上去,由于此demo整体功能较复杂,因此使用第三方的东西,ListView条目布局如下:
        
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="6dp" >
    
        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/avator"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:src="@drawable/empty_photo" />
    
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/avator"
            android:textColor="#576B95"
            android:textSize="16sp"
            android:text="name" />
    
        <TextView
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/name"
            android:layout_marginLeft="10dp"
            android:textSize="12sp"
            android:layout_toRightOf="@id/avator"
            android:text="content" />
    
        <com.loveplusplus.demo.image.NoScrollGridView
            
            android:id="@+id/gridView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingTop="5dp"
            android:layout_below="@id/content"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/avator"
            android:horizontalSpacing="1dp"
            android:numColumns="3"
            
            android:visibility="gone"
            android:verticalSpacing="1dp" />
    
    </RelativeLayout>

       接下来我们还需要将主流的photoView.jar加入到工程中,
       总结一下实现以上功能我们使用了第三的imagloader,支持手势缩放的PhotoView,圆形图像的circleimageView,熟悉安卓view绘制机制加载过程,事件传递和分发的朋友是不需要第三方开源项目的支持的,但是对于入门不久的同学,学会怎样使用开源框架就可以,但是想要提高开源项目的的核心还是需要了解的,欢迎阅读
      运行效果图:
      
      有兴趣的朋友建议阅读下:
         安卓事件机制(一)和上篇关于View的博文。谢谢交流和分享。
    
    
    

    demo源码下载地址:https://github.com/Tamicer/CHatMomentDemo

       
    
    
    
    展开全文
  • 朋友动态发布时间计算转载请注明:IT_xiao小巫,http://blog.csdn.net/wwj_748如果觉得博文不错,请移步到2014年博客之星投票地址:http://vote.blog.csdn.net/blogstar2014/details?username=wwj_748#content ...
    Android开发记录19-朋友圈动态发布时间计算

    转载请注明:IT_xiao小巫,http://blog.csdn.net/wwj_748

    前面的一篇文章给大家关于时间知识点的分享http://blog.csdn.net/wwj_748/article/details/11536421,本篇博客笔者想给大家分享的事如何通过Calendar来计算我们平时需要用到的年、月、日、时、分、秒等,还有如何计算类似朋友圈动态发布的时间。
    这里要大家去了解的几个重要的类:
    日历类:Calendar
    日期类:Date
    日期格式类:SimpleDateFormat
    基本上我们对日期的一些转换都是在这几个类中进行的,有以下几个常用的转换:
    通过Calendar获取相关日期信息,比如当前的年月日,去年的年月日、下一年的年月日等,这里可以查看笔者关于自定义日历的一篇博文,有提供相关的日期工作类:http://blog.csdn.net/wwj_748/article/details/42244865
    Date与Calendar之间的转换
    Date与日期字符串的互相转换


    首先我们来看如何使用Calendar来获取我们想要的日期信息:

    获取当前日历:
    Calendar calendar = Calendar.getInstance();

    根据字段来获取信息
    获取当前年份:
    Calendar.getInstance().get(Calendar.YEAR)


    获取当前月:
    Calendar.getInstance().get(Calendar.MONTH) + 1
    注意这里要+1,因为月份是从0开始的。
    获取当前月的第几天:
    calendar.get(Calendar.DAY_OF_MONTH)
    获取当前时间毫秒显示:
    calendar.getTimeInMillis()



    Calendar还可以做很多一些运算,笔者在开发日历控件的时候,就做了很多关于日期的运算:
    通过Calendar的add方法来对指定字段进行运算,举例如下:
    // 获取去年的年份
    	public static int getLastYear() {
    		Calendar c = Calendar.getInstance();
    		c.add(Calendar.YEAR, -1);
    		return c.get(Calendar.YEAR);
    	}
    	
    	// 获取下一年年份
    	public static int getNextYear() {
    		Calendar c = Calendar.getInstance();
    		c.add(Calendar.YEAR, 1);
    		return c.get(Calendar.YEAR);
    	}

    // 获取上一个月的月份
    	public static int getLastMonth() {
    		Calendar c = Calendar.getInstance();
    		c.add(Calendar.MONTH, -1);
    		return c.get(Calendar.MONTH) + 1;
    	}
    	
    	// 获取下一个月的月份
    	public static int getNextMonth() {
    		Calendar c = Calendar.getInstance();
    		c.add(Calendar.MONTH, 1);
    		return c.get(Calendar.MONTH) + 1;
    	}


    关于Calendar还有很多字段,希望读者查看API自己去尝试。



    Date与Calendar之间的转换
    我们可以通过调用Calendar的setTime方法来设置时间所对应的日历对象
    calendar.setTime(new Date())

    Date与日期字符串的互相转换
    在实际开发中,我们经常需要对服务端返回的日期数据进行格式化,这里就需要用到SimleDateFormat,我们可以指定日期格式:
    	private static SimpleDateFormat DATE_FORMAT_TILL_SECOND = new SimpleDateFormat(
    			"yyyy-MM-dd HH:mm:ss");

    我们得到一个日期字符串,可以将其转换为Date对象:
    /**
    	 * 日期字符串转换为Date
    	 * @param dateStr
    	 * @param format
    	 * @return
    	 */
    	public static Date strToDate(String dateStr, String format) {
    		Date date = null;
    
    		if (!TextUtils.isEmpty(dateStr)) {
    			DateFormat df = new SimpleDateFormat(format);
    			try {
    				date = df.parse(dateStr);
    			} catch (ParseException e) {
    				e.printStackTrace();
    			}
    		}
    		return date;
    	}
    	


    也可以将我们获取的Date对象转换为指定格式的字符串:
    /**
    	 * 日期转换为字符串
    	 * @param timeStr
    	 * @param format
    	 * @return
    	 */
    	public static String dateToString(String timeStr, String format) {
    		// 判断是否是今年
    		Date date = DateHelper.strToDate(timeStr, format);
    		Calendar calendar = Calendar.getInstance();
    		calendar.setTime(date);
    		// 如果是今年的话,才去“xx月xx日”日期格式
    		if (calendar.get(Calendar.YEAR) == getYear()) {
    			return DATE_FORMAT_TILL_DAY_CURRENT_YEAR.format(date);
    		}
    		
    		return DATE_FORMAT_TILL_DAY_CH.format(date);
    	}


    最后要说的就是,开发中我们需要计算一条动态的发布时间,一般我们得到的是一条日期字符串,我们需要计算当前时间和动态的发布时间,计算它们的差值来判断发布在什么时候,上代码:
    /**
    	 * 日期逻辑
    	 * @param dateStr 日期字符串
    	 * @return
    	 */
    	public static String timeLogic(String dateStr) {
    		Calendar calendar = Calendar.getInstance();
    		calendar.get(Calendar.DAY_OF_MONTH);
    		long now = calendar.getTimeInMillis();
    		Date date = strToDate(dateStr, DATE_FORMAT);
    		calendar.setTime(date);
    		long past = calendar.getTimeInMillis();
    		
    		// 相差的秒数
    		long time = (now - past) / 1000;
    		
    		StringBuffer sb = new StringBuffer();
    		if (time > 0 && time < 60) { // 1小时内
    			return sb.append(time + "秒前").toString();
    		} else if (time > 60 && time < 3600) {
    			return sb.append(time / 60+"分钟前").toString();
    		} else if (time >= 3600 && time < 3600 * 24) {
    			return sb.append(time / 3600 +"小时前").toString();
    		}else if (time >= 3600 * 24 && time < 3600 * 48) {
    			return sb.append("昨天").toString();
    		}else if (time >= 3600 * 48 && time < 3600 * 72) {
    			return sb.append("前天").toString();
    		}else if (time >= 3600 * 72) {
    			return dateToString(dateStr, DATE_FORMAT);
    		}
    		return dateToString(dateStr, DATE_FORMAT);
    	}

    效果类似这种:
    本文的记录到这里,这里提一点,开发中我们经常遇到的一些知识点是可以整理和总结的,移动的产品开发是需要快递迭代的,对这些开发知识点的记录对自己有很大的帮助,提升你自己的开发效率,对应的你的能力就提升了,薪资也会跟着往上升,小巫个人的一点观点,感谢你的耐心阅读。




    展开全文
  • Android仿微信朋友圈发布动态功能

    千次阅读 2020-03-27 10:33:58
    应工作上的要求,需要有一个类似于微信朋友圈发动态上传图片的功能,想起曾经已经做过了,但奈何不忍看自己以前写的代码的惨状,觉得重新封装一个使用方便,易于维护的类似功能的类,自己之后用起来也顺手,当然也...

    一、前言

    应工作上的要求,需要有一个类似于微信朋友圈发动态上传图片的功能,想起曾经已经做过了,但奈何不忍看自己以前写的代码的惨状,觉得重新封装一个使用方便,易于维护的类似功能的类,自己之后用起来也顺手,当然也方便一下大家,这样可以加快我们工作的效率,让我们有更多的时间学习(划水)。

    image

    功能上的话,目前有添加图片、查看大图、删除图片

    二、效果图

    先贴一下效果图吧

    image

    三、实现功能

    库有用到butterknife和显示图片的glide
    适配器用的rvadapter,加一个删除功能的气泡弹窗
    引入一下相关依赖:

        api 'com.jakewharton:butterknife:10.2.1'
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    
        implementation 'com.github.bumptech.glide:glide:4.5.0'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
    
        //气泡弹窗
        implementation 'me.kareluo.ui:popmenu:1.1.0'
        implementation 'com.zhy:base-rvadapter:3.0.3'
    

    ####(一)布局文件
    主页面上的话,只有一个RecyclerView,他的LayoutManager设为GridLayoutManager,一行为3个

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_images"
            android:paddingTop="15dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    子布局的话,有两个ImageView,一个是普通的图片,另外一个固定为那个添加的按钮

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:gravity="center"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">
    
        <ImageView
            android:id="@+id/iv_thum"
            android:visibility="gone"
            android:scaleType="centerCrop"
            android:layout_width="90dp"
            android:layout_height="match_parent" />
    
        <ImageView
            android:visibility="gone"
            android:id="@+id/iv_add"
            android:src="@mipmap/add_icon"
            android:scaleType="centerCrop"
            android:layout_width="90dp"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    
    

    (二)Activity代码

    先来看一下如何在我们的页面里使用我已经写好的这个adapter

    首先初始化一下控件和adapter,给adapter设置一个点击添加图片的监听

        private void initView() {
            rvImages.setLayoutManager(new GridLayoutManager(this, 3));
            adapter = new NineGridAdapter(MainActivity.this, mSelectList, rvImages);
            adapter.setMaxSize(maxNum);
            rvImages.setAdapter(adapter);
            adapter.setOnAddPicturesListener(new OnAddPicturesListener() {
                @Override
                public void onAdd() {
                    pickImage();
                }
            });
        }
    

    选择图片页面的启动

        private void pickImage() {
            MultiImageSelector selector = MultiImageSelector.create(context);
            selector.showCamera(true);
            selector.count(maxNum);
            selector.multi();
            selector.origin(mSelectList);
            selector.start(instans, REQUEST_IMAGE);
        }
    

    拿到选择图片页面的返回数据,添加至List中

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_IMAGE) {
                if (resultCode == RESULT_OK) {
                    List<String> select = data.getStringArrayListExtra(MultiImageSelector.EXTRA_RESULT);
                    mSelectList.clear();
                    mSelectList.addAll(select);
                    adapter.notifyDataSetChanged();
                }
            }
        }
    

    (三)适配器代码

    首先在构造函数中,往list里添加一个空串,为添加按钮占位。之后初始化一下删除气泡按钮跟大图显示的控件

        public NineGridAdapter(Context context, List<String> selectPath, RecyclerView rvImages) {
            super(context, R.layout.item_img, selectPath);
            this.context = context;
    
            selectPath.add("");
            initDeleteMenu();
            initTransfer(rvImages);
        }
    

    两个控件初始化的代码

        /**
         * 初始化大图查看控件
         */
        private void initTransfer(RecyclerView rvImages) {
            transferee = Transferee.getDefault(context);
            config = TransferConfig.build()
                    .setSourceImageList(getDatas())
                    .setProgressIndicator(new ProgressBarIndicator())
                    .setIndexIndicator(new NumberIndexIndicator())
                    .setImageLoader(GlideImageLoader.with(context.getApplicationContext()))
                    .setJustLoadHitImage(true)
                    .bindRecyclerView(rvImages, R.id.iv_thum);
        }
    
        /**
         * 初始化图片删除小弹窗
         */
        private void initDeleteMenu() {
            menuView = new PopupMenuView(context, R.menu.menu_pop, new MenuBuilder(context));
            menuView.setSites(PopupView.SITE_TOP);
            menuView.setOnMenuClickListener(new OptionMenuView.OnOptionMenuClickListener() {
                @Override
                public boolean onOptionMenuClick(int position, OptionMenu menu) {
                    getDatas().remove(deletePosition);
                    if (!getDatas().get(getDatas().size() - 1).equals("")) {
                        //列表最后一张不是添加按钮时,加入添加按钮
                        getDatas().add("");
                    }
                    notifyDataSetChanged();
                    return true;
                }
            });
        }
    

    在item填充的函数中完成图片的显示,点击和长按的监听

        @Override
        protected void convert(ViewHolder viewHolder, String item, final int position) {
            ImageView ivThum = viewHolder.getView(R.id.iv_thum);
            ImageView ivAdd = viewHolder.getView(R.id.iv_add);
            if (item.equals("")) {
                //item为添加按钮
                ivThum.setVisibility(View.GONE);
                ivAdd.setVisibility(View.VISIBLE);
            } else {
                //item为普通图片
                ivThum.setVisibility(View.VISIBLE);
                ivAdd.setVisibility(View.GONE);
            }
            Glide.with(mContext).load(item).into(ivThum);
            ivThum.setOnClickListener(new PicturesClickListener(position));
            ivAdd.setOnClickListener(new PicturesClickListener(position));
    
            ivThum.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    deletePosition = position;
                    //最上面的三个删除按钮是往下的  其他的都是往上的
                    if (position < 3) {
                        menuView.setSites(PopupView.SITE_BOTTOM);
                    } else {
                        menuView.setSites(PopupView.SITE_TOP);
                    }
                    menuView.show(view);
                    return false;
                }
            });
        }
    

    点击事件代码

        private class PicturesClickListener implements View.OnClickListener {
    
            int position;
    
            public PicturesClickListener(int position) {
                this.position = position;
            }
    
            @Override
            public void onClick(View view) {
                switch (view.getId()) {
                    case R.id.iv_thum:
                        //点击图片
                        config.setNowThumbnailIndex(position);
                        config.setSourceImageList(getDatas());
                        transferee.apply(config).show();
                        break;
                    case R.id.iv_add:
                        //点击添加按钮
                        if (listener != null)
                            listener.onAdd();
                        break;
                }
            }
        }
    

    #四、关于老v7项目的问题
    前段时间也是刚把自己的项目从v7手动改为androidx的,忙活了半天,后来发现android studio有一键将v7项目改为新的androidx的项目,算是给自己记个笔记
    78M(FMFGHPU%CF1GDL_POBM.png

    有问题和建议都可以在评论区给我留言哦

    源码地址:https://github.com/Giftedcat/DynamicPublishing

    展开全文
  • 最近在做毕设,想写一个保护图片隐私的平台,具体就是在这个平台上好友之间 查看图片可以直接看到未...感觉和微信朋友圈中好友能访问自己的动态,非好友访问不到有些相似,想问下 各位大神这种功能是否是访问控制技术
  • 前天给大家分享了如何利用...我们需要获取的数据是朋友圈和发布日期,因此在这里定义好日期和动态两个属性,如下图所示。2、修改实现爬虫逻辑的主文件moment.py,首先要导入模块,尤其是要主要将items.py中的WeixinM...
  • 关于动态调用动态库方法说明

    千次阅读 2014-07-25 00:36:56
    关于动态调用动态库方法说明 一、 动态库概述 1、 动态库的概念 日常编程中,常有一些函数不需要进行编译或者可以在多个文件中使用(如数据库输入/输出操作或屏幕控制等标准任务函数)。可以事先对这些函数进行...
  • 算法-动态规划 Dynamic Programming--从菜鸟到老鸟

    万次阅读 多人点赞 2017-07-15 22:58:29
    前言最近在牛客网上做了几套公司的真题,发现有关动态规划(Dynamic Programming)算法的题目很多。相对于我来说,算法里面遇到的问题里面感觉最难的也就是动态规划(Dynamic Programming)算法了,于是花了好长时间...
  • 轻松学,Java 中的代理模式及动态代理

    万次阅读 多人点赞 2017-06-29 22:08:55
    前几天我写了《秒懂,Java 注解 (Annotation)你可以这样学》,因为注解其实算反射技术中的一部分,然后我想了一下,反射技术中还有个常见的概念就是动态代理,于是索性再写一篇关于动态代理的博文好了。...
  • 关于动态壁纸商城的撸码

    千次阅读 2016-05-22 09:21:25
    之前想写一个商城类的动态壁纸app,但是一直没有找到资料,网上的很多资料都是直接做成一个壁纸,然后去壁纸设置里面去更换,这显然不是我想要的类型,然后经过一番找资料,终于让我弄好了,暂且先写下来,如果有...
  • 最近做完朋友圈功能后,测试提出一个功能优化,在某些发布的动态下评论时软键盘和输入框遮当内容了,这个用户体验感觉不是很好,于是根据今日头条和其他热门的App评论时软键盘和输入框都是在内容下面.Scroller是一个...
  • 关于代码动态修改xib内控件尺寸

    千次阅读 2016-06-07 10:51:54
    本文只是作为对自己学习的一个记录,希望能帮到一些朋友
  • 动态代理

    千次阅读 2018-08-30 19:06:38
    1.代理模式深入学习(一)——动态代理的实现及解析 ... 一、代理模式 分为静态和动态代理。静态代理,我们通常都很熟悉。有一个写好的代理类,实现与要代理的类的一个共同的... 这里主要想说的是关于动态代理。...
  • iOS 关于navigationBar的一些:毛玻璃、透明、动态缩放、动态隐藏
  • 新浪微博、微信朋友圈、QQ动态都具有让用户个体发布内容的功能,但在产品的表现上却不尽相同; 功能设计对比: 本文在对比这三者关于发布功能的产品设计时 ,主要关注发布纯文字消息和发布图文消息两种,三者发布...
  • Apollo 关于ConfigurationProperties 的动态刷新

    万次阅读 热门讨论 2019-01-23 16:25:47
    有需要的朋友可以查看我的上一个博文 https://blog.csdn.net/HiBoyljw/article/details/86495531 我们都知道,springboot中的配置文件中的配置属性丢到Apollo之后,我们在Apollo中修改属性 那么应用就能够自动刷新...
  • 关于mybatis的动态建表功能的实现
  • 在此我们项目中用到的关于继承activity的内容过多,如果冒然修改为继承AppCompatActivity的话我们的工作量将会相当的大,所有我们的从jcplayer的源码入手解决问题,让他来兼容我们的项目。 public static ...
  • 朋友圈是基于微信社交...高频使用:日常使用中,76.4%的用户会使用朋友圈来查看朋友动态或进行分享。微信已经融入用户的生活,成为用户感官情感的延伸。朋友圈广告将会通过微信广告系统进行投放和管理,广告本身内...
  • 关于使用动态链接库一点感想DOTNET中,对于某些动态链接库,并不能用添加引用来调用,所以感觉微软这方面做得的确欠考虑,没办法中的办法是搞了一个东东可以调用,但是前提你需要有该动态库的全部文档,否则还是玩不...
  • 关于动态执行代码(js的Eval)

    千次阅读 2012-02-03 09:56:55
    熟悉javascript的朋友对Eval()函数可能都不会陌生,我们可以用它来实现动态代码的执行,我自己甚至写过一个网页专门用来计算算术表达式的,计算能力上比google、baidu的 计算器还要好一些,至少精度要高,但是如果...
  • 前几天看到一篇关于.net动态编译的文章 .NET中的动态编译 ,很受启发。在此基础上我做了一些封装,为使调用更加简单,并增加了对动态代码调试的支持,相同代码只编译一次的支持,代码改动自动重新编译,代码引用...
  • 现在遇到一个问题,在用indesign生成的动态pdf的文本框不可以自动改变文本框的大小,而且紧跟的文字也不能随前面的本本框大小的变化而移动。 用adobe liveCycle Designer可以生成根据输入文字多少而自动变换大小的...
  • 动态规划算法

    千次阅读 2018-10-17 22:25:52
    最近在牛客网上做了几套公司的真题,发现有关动态规划(Dynamic Programming)算法的题目很多。相对于我来说,算法里面遇到的问题里面感觉最难的也就是动态规划(Dynamic Programming)算法了,于是花了好长时间,...
  • 最火的瓜,得用动态规划来吃

    万次阅读 多人点赞 2020-04-23 21:45:46
    全网络都是关于“小猪”的新闻,这前女友公开吃瓜确实强悍! 一、事件的前因后果 周做为“小猪“”的前女友,在微博官宣与罗志祥分手。 我帮你快速总结一下就是周看了“小猪”的手机发现了以下关键信息: 1. 有一...
  • 于是找了很多博客后,加上自己的实际实践,总结了feign组件的动态调用,可以方便我们的程序开发,省去很多不必要的代码,但由此可能会带来一些feign的性能问题,没有详细测试中,希望使用的朋友们不要忘了测试一下...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 107,903
精华内容 43,161
关键字:

朋友有关的动态