Android之微信图片加载

MainActivity

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.example.imageloader.bean.ImageAdapter;
import com.example.imageloader.utils.ImageLoader;
import com.example.imageloader.utils.ImageLoader.Type;
import com.example.imageloader.utils.ImagePopupWindow;
import com.example.imageloader.utils.ImagePopupWindow.OnSelectedListener;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private GridView mGridView;
    private List<String> mLists;
    private ImageAdapter mImgAdapter;

    private RelativeLayout mBottomLy;
    private TextView mDirName;
    private TextView mDirCount;

    private File mCurrentDir;
    private int mMaxcount;

    private ProgressDialog mPd;

    private List<FolderBean> mFolderBeans = new ArrayList<FolderBean>();
    private ImagePopupWindow mPopupWindow;

    private Handler handler=new Handler(){
        public void handleMessage(android.os.Message msg) {
            if(msg.what== 0x110){
                mPd.dismiss();
                data2View();//绑定数据到view中;

                initPopupWindow();
            }
        }


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

        initView();
        initDatas();
        initEvent();
    }

    protected void initPopupWindow() {
        mPopupWindow=new ImagePopupWindow(this, mFolderBeans);
        mPopupWindow.setOnDismissListener(new OnDismissListener() {

            @Override
            public void onDismiss() {
                ligthOn();

            }
        });
        //接口回调 
        mPopupWindow.setPopSelected(new OnSelectedListener() {

            @Override
            public void onSelected(FolderBean folderBean) {
                // TODO Auto-generated method stub
                mCurrentDir=new File(folderBean.getDir());
            mLists=Arrays.asList(   mCurrentDir.list(new FilenameFilter() {

                    @Override
                    public boolean accept(File dir, String filename) {
                        if(filename.endsWith(".jpg")||filename.endsWith(".jpeg")||filename.endsWith(".png")  ){
                            return true;
                        }

                        return false;
                    }
                }));

            mImgAdapter=new ImageAdapter(MainActivity.this, mLists, mCurrentDir.getAbsolutePath());
            mGridView.setAdapter(mImgAdapter);

            mDirCount.setText(mLists.size()+"");
            mDirName.setText(folderBean.getName());

            mPopupWindow.dismiss();
            }
        });

    }
/**
 * 内容区域变亮
 */
    protected void ligthOn() {
        WindowManager.LayoutParams lp=getWindow().getAttributes();
        lp.alpha=1.0f;
        getWindow().setAttributes(lp);

    }

    protected void data2View() {
        if(mCurrentDir==null){
            Toast.makeText(this, "未扫描到任何图片", Toast.LENGTH_SHORT).show();
            return;
        }
        mLists=Arrays.asList(mCurrentDir.list());
        mImgAdapter=new ImageAdapter(this, mLists, mCurrentDir.getAbsolutePath());
        mGridView.setAdapter(mImgAdapter);

        mDirCount.setText(mMaxcount+"");
        mDirName.setText(mCurrentDir.getName());

    }

    private void initView() {
        mGridView = (GridView) findViewById(R.id.gridView);
        mBottomLy = (RelativeLayout) findViewById(R.id.rl_bottom);
        mDirName = (TextView) findViewById(R.id.id_dir_name);
        mDirCount = (TextView) findViewById(R.id.id_dir_count);

    }



    // ContentProvite扫描手机中所有图片;
    private void initDatas() {
        if (!(Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED))) {
            Toast.makeText(this, "SD卡不可用", Toast.LENGTH_SHORT).show();
            return;
        }

        mPd = ProgressDialog.show(this, null, "正在加载...");
        new Thread() {
            public void run() {
                Uri mImgUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;//获取媒体库图片库uri

                ContentResolver cr = MainActivity.this.getContentResolver();
                //------------------这里的 + "= ? or " +要注意,格式要对
                Cursor cursor=cr.query(mImgUri, null, MediaStore.Images.Media.MIME_TYPE
                        + "= ? or " + MediaStore.Images.Media.MIME_TYPE 
                        + "= ? ",new String[] { "image/jpeg", "image/png" },
                        MediaStore.Images.Media.DATE_MODIFIED);

                Set<String> mDirPaths=new HashSet<String>();//储存遍历过的防止重复遍历
                while(cursor.moveToNext()){
                    String path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));//获取每张图片的路径
                    File parentFile=new File(path).getParentFile();//获取图片的父文件夹
                    if(parentFile==null){
                        continue;//结束本次循环,进行下一次循环
                    }
                    String dirPath=parentFile.getAbsolutePath();//获取此文件夹路径;

                    FolderBean folderBean=null;
                    //如果包含某个元素
                    if(mDirPaths.contains(dirPath)){
                        continue;//如果包含,结束循环
                    }else{
                        mDirPaths.add(dirPath);
                        folderBean=new FolderBean();
                        folderBean.setDir(dirPath);
                        folderBean.setFirstImagePath(path);                                             
                    }

                    if(parentFile.list()==null){
                        continue;
                    }
                    //获取文件夹中图片数目,有删选
                    int picSize=parentFile.list(new FilenameFilter() {

                        @Override
                        public boolean accept(File dir, String filename) {
                            if(filename.endsWith(".jpg")||filename.endsWith(".jpeg")||filename.endsWith(".png")  ){
                                return true;
                            }

                            return false;
                        }
                    }).length;

                    folderBean.setCount(picSize);
                    mFolderBeans.add(folderBean);
                    //当前文件夹的名称数量。应该扫描一次加一张
                    if(picSize>mMaxcount){
                        mMaxcount=picSize;
                        mCurrentDir=parentFile;
                    }

                }
                cursor.close();
                //通知handler扫描图片完成
                handler.sendEmptyMessage(0x110);
            };
        }.start();
    }

    private void initEvent() {
      mBottomLy.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            //mPopupWindow.setAnimationStyle(animationStyle);
            mPopupWindow.showAsDropDown(mBottomLy, 0, 0);
            lightOff();
        }
    });
    }

    protected void lightOff() {
        WindowManager.LayoutParams lp=getWindow().getAttributes();
        lp.alpha=0.3f;
        getWindow().setAttributes(lp);

    }

}

ImagePopupWindow

import java.util.List;

import com.example.imageloader.FolderBean;
import com.example.imageloader.R;
import com.example.imageloader.utils.ImageLoader.Type;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;

public class ImagePopupWindow extends PopupWindow {

    private int mWidth;
    private int mHeight;
    private View mConvertView;
    private ListView mListView;
    private List<FolderBean> mLists;
    //接口回调
    private OnSelectedListener mOnSelectedListener;
    public interface OnSelectedListener{
        void onSelected(FolderBean folderBean);
    }
    public void setPopSelected(OnSelectedListener mOnSelectedListener){
        this.mOnSelectedListener=mOnSelectedListener;
    }

    public ImagePopupWindow(Context context,List<FolderBean> mLists) {

        callWidthAndHeight(context);

        mConvertView=LayoutInflater.from(context).inflate(R.layout.popup, null);
        this.mLists=mLists;

        setContentView(mConvertView);
        setWidth(mWidth);
        setHeight(mHeight);
        setFocusable(true);
        setTouchable(true);
        setOutsideTouchable(true);
        setBackgroundDrawable(new BitmapDrawable());
        setTouchInterceptor(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(event.getAction()==MotionEvent.ACTION_OUTSIDE){
                    dismiss();
                    return true;
                }
                return false;
            }
        });

        initView(context);
        initEvent();
    }
private void initView(Context context) {
    mListView=(ListView) mConvertView.findViewById(R.id.lv) ;
    mListView.setAdapter(new ListDieAdapter(context, mLists));

    }
private void initEvent() {
        mListView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int position,
                    long arg3) {

                mOnSelectedListener.onSelected(mLists.get(position));//接口回调
            }

        });

    }
/**
 * 计算popupWindow的宽度和高度
 * @param context
 */
    private void callWidthAndHeight(Context context) {
        WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm=new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);

        mWidth=dm.widthPixels;
        mHeight=(int) (dm.heightPixels*0.7);
    }

public class ListDieAdapter extends ArrayAdapter<FolderBean>{
    private LayoutInflater mInflater;
    //private List<FolderBean> mDatas;

    public ListDieAdapter(Context context, 
             List<FolderBean> mLists) {
        super(context, 0,  mLists);

        mInflater=LayoutInflater.from(context);

    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder;
        if(convertView==null){
            holder=new ViewHolder();
            convertView=mInflater.inflate(R.layout.popup_item, parent, false);

            holder.mImg=(ImageView) convertView.findViewById(R.id.pop_item_iv);
            holder.mDirName=(TextView) convertView.findViewById(R.id.pop_item_name);
            holder.mDirCount=(TextView) convertView.findViewById(R.id.pop_item_count);
            convertView.setTag(holder);
        }else{
            holder=(ViewHolder) convertView.getTag();
        }

        FolderBean fb=getItem(position);
        holder.mImg.setImageResource(R.drawable.ic_launcher);
        ImageLoader.getInstance(3,Type.LIFO).loadImage(fb.getFirstImagePath(), holder.mImg);
        holder.mDirCount.setText(fb.getCount()+"");
        holder.mDirName.setText(fb.getName());

        return convertView;
    }
    private class ViewHolder{
        ImageView mImg;
        TextView mDirName;
        TextView mDirCount;
    }

}
}

ImageAdapter

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;

import com.example.imageloader.R;

import com.example.imageloader.utils.ImageLoader;
import com.example.imageloader.utils.ImageLoader.Type;


public class ImageAdapter extends BaseAdapter{
        private static Set<String> mSelectedImg=new HashSet<String>();

        private String mDirPath;
        private List<String> mImgPaths;
        private LayoutInflater mInflater;

        public ImageAdapter(Context context,List<String> mImgPaths,String mDirPath){
            this.mDirPath=mDirPath;
            this.mImgPaths=mImgPaths;
            mInflater=LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return mImgPaths.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return mImgPaths.get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            final ViewHolder holder;
            if(convertView==null){
            holder=new ViewHolder();
            convertView=mInflater.inflate(R.layout.item_gridview, parent,false);
            holder.mImg=(ImageView) convertView.findViewById(R.id.iv);
            holder.mSelect=(ImageButton) convertView.findViewById(R.id.ib);
            convertView.setTag(holder);


            }else{
                holder=(ViewHolder) convertView.getTag();
            }
            //重置状态
            holder.mImg.setImageResource(R.drawable.ic_launcher);
            holder.mSelect.setImageResource(R.drawable.a0a);
            holder.mImg.setColorFilter(null);


            ImageLoader.getInstance(3, Type.LIFO).loadImage(mDirPath+ "/" +mImgPaths.get(position), holder.mImg);
            final String filePath=mDirPath+ "/" +mImgPaths.get(position);   

            holder.mImg.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {

                    if(mSelectedImg.contains(filePath)){
                        mSelectedImg.remove(filePath);
                        holder.mImg.setColorFilter(null);
                        holder.mSelect.setImageResource(R.drawable.a0a);

                    }else{
                        mSelectedImg.add(filePath);
                        holder.mImg.setColorFilter(Color.parseColor("#77000000"));
                        holder.mSelect.setImageResource(R.drawable.a0_);
                    }
                    //notifyDataSetChanged();
                }
            });

            if(mSelectedImg.contains(filePath)){
                holder.mImg.setColorFilter(Color.parseColor("#77000000"));
                holder.mSelect.setImageResource(R.drawable.a0_);
            }

            return convertView;
        }
        class ViewHolder{
            ImageView mImg;
            ImageButton mSelect;
        }

    }

FolderBean

public class FolderBean {
    //当前文件夹路径
    private String dir;
    private String firstImagePath;
    private String name;
    private int count;

    public FolderBean() {

    }

    public String getDir() {
        return dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
        //获取路径最后一个“/” 的索引
        int lastIndexOf=this.dir.lastIndexOf("/");
        this.name=this.dir.substring(lastIndexOf+1);
    }

    public String getFirstImagePath() {
        return firstImagePath;
    }

    public void setFirstImagePath(String firstImagePath) {
        this.firstImagePath = firstImagePath;
    }

    public String getName() {
        return name;
    }

//  public void setName(String name) {
//      this.name = name;
//  }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }


}

ImageLoader

import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;

public class ImageLoader {
/**
 * 图片加载类
 */

    private static ImageLoader mInstance;//单例模式
    private LruCache<String, Bitmap> mLruCache;//缓存

    private ExecutorService mThreadPool;//线程池
    private static final int THREAD_COUNT=1;//默认线程数
    private Thread mPoolThread;//后台轮询线程
    private Handler mPoolThreadHandler;//给线程发送消息的handler
    private Handler mUIHandler;//更新图片UI的handler

    private Type mType=Type.LIFO;//默认加载方式,队列调度方式
    private LinkedList<Runnable> mTaskQueue;//任务队列

    private Semaphore sePoolTH=new Semaphore(0);//信号量,解决并发问题
    private Semaphore sePoolT;//利用信号量控制任务队列

    //加载策略方式
    public enum Type{
        FIFO,LIFO
    }

    //初始化,可以让用户指定线程数,加载方式
    private ImageLoader(int threadCont,Type type) {
        init(threadCont,type);
    }
    private void init(int threadCont, Type type) {
        //后台轮询线程
        mPoolThread=new Thread(){
            @Override
            public void run() {
                Looper.prepare();//启动
                mPoolThreadHandler=new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                    //当接收到消息,从线程池取出任务
                        mThreadPool.execute(getTask());

                        try {
                            sePoolT.acquire();//接收信号,开始执行。没有信号就阻塞,从而控制队列
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }

                };
                sePoolTH.release();//释放一个信号量,说明已经执行好了,可以执行sePoolTH.acquire()了
                Looper.loop();//在后台不断的进行轮询
            }
        };
        mPoolThread.start();

        int maxMemory=(int) Runtime.getRuntime().maxMemory();//获取最大可用内存
        int cacheMemory=maxMemory/8;  //取8分之1
        mLruCache=new LruCache<String, Bitmap>(cacheMemory){
            @Override//测量每个biamap的值
            protected int sizeOf(String key, Bitmap value) {
                // TODO Auto-generated method stub
                return value.getRowBytes()*value.getHeight();
            }
        };
        //初始化线程池
        mThreadPool=Executors.newFixedThreadPool(threadCont);

        mTaskQueue=new LinkedList<Runnable>();
        mType=type;

        sePoolT=new Semaphore(threadCont);//只能同时并发threadCont个,多的阻塞
    }
    //从任务队列取出一个方法
        private Runnable getTask() {
            if(mType==Type.FIFO){
                return mTaskQueue.removeFirst();
            }else if(mType==Type.LIFO){
                return mTaskQueue.removeLast();
            }
            return null;
        }

    //单例
    public static ImageLoader getInstance(int threadCont, Type type){
        if(mInstance==null){
            synchronized (ImageLoader.class) {
                if(mInstance==null){
                    mInstance=new ImageLoader(THREAD_COUNT,Type.LIFO);//默认的参数
                }
            }
        }
        return mInstance;       
    }

    //根据path为imageView设置图片
    public void loadImage(final String path,final ImageView imageView){

        imageView.setTag(path);//
        if(mUIHandler==null){
            mUIHandler=new Handler(){
                public void handleMessage(Message msg) {
                    //获取得到的图片,设置到imageview
                    ImgBeanHolder holder=(ImgBeanHolder) msg.obj;
                    Bitmap bitmap=holder.bitmap;
                    ImageView imageView=holder.imageView;
                    String path=holder.path;

                    if(imageView.getTag().toString().equals(path)){
                        imageView.setImageBitmap(bitmap);
                    }
                };
            };
        }
        //根据path在缓存中获取bitmap
        Bitmap bm=getBitmapFromLruCache(path);
        if(bm!=null){
            Message message=Message.obtain();
            ImgBeanHolder holder=new ImgBeanHolder();//这样下面这三个绝对是对应的。
            holder.bitmap=bm;
            holder.path=path;
            holder.imageView=imageView;
            message.obj=holder;
            mUIHandler.sendMessage(message);
        }else{
            addTasks(new Runnable() {

                @Override
                public void run() {
                    //加载图片 
                    //1,获取图片需要显示的大小
                ImageSize imageSize=getImageViewSize(imageView);
                    //2,压缩图片
                Bitmap bm=decodeSampledBitmap(path,imageSize.width,imageSize.height);
                   //3,把图片加入到缓存
                    addBitampLruCache(path,bm);

                    Message message=Message.obtain();
                    ImgBeanHolder holder=new ImgBeanHolder();//这样下面这三个绝对是对应的。
                    holder.bitmap=bm;
                    holder.path=path;
                    holder.imageView=imageView;
                    message.obj=holder;
                    mUIHandler.sendMessage(message);

                    sePoolT.release();//释放信号量
                }
            });
        }
    }

    protected void addBitampLruCache(String path, Bitmap bm) {
        if(getBitmapFromLruCache(path)==null){
            if(bm!=null){
                mLruCache.put(path, bm);
            }
        }

    }
    //根据图片需要显示的宽和高对图片进行压缩
    protected Bitmap decodeSampledBitmap(String path, int width, int height) {
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        BitmapFactory.decodeFile(path, options);

        options.inSampleSize=caculateInSampleSize(options,width,height);
        //使用获取到insampleSize再次解析图片;
        options.inJustDecodeBounds=false;
        Bitmap bitmap=BitmapFactory.decodeFile(path, options);

        return bitmap;
    }
    //计算压缩比
    private int caculateInSampleSize(Options options, int reqwidth, int reqheight) {
        int width=options.outWidth;   //图片的实际大小,要进行压缩的
        int height=options.outHeight;

        int sampleSize=1;
        //如何图片的大小大于需求的大小,则进行压缩
        if(width>reqwidth||height>reqheight){
            int widthbili=Math.round(width*1.0f/reqwidth);
            int heightbili=Math.round(height*1.0f/reqheight);

             sampleSize=Math.max(widthbili, heightbili);
        }
        return sampleSize;
    }
    //根据imageview获取适当的压缩的宽和高 
    protected ImageSize getImageViewSize(ImageView imageView) {
        ImageSize imageSize=new ImageSize();
        DisplayMetrics dm= imageView.getContext().getResources().getDisplayMetrics();//获取屏幕


        LayoutParams lp=imageView.getLayoutParams();
        int width= imageView.getWidth();//获取实际宽度
        if(width<=0){
            width=lp.width;//获取imageview在layout声明的宽度
        }
        if(width<=0){
            //width=imageView.getMaxWidth();//检查最大值
            width= getImageValue(imageView, "mMaxWidth");
        }
        if(width<=0){
            width=dm.widthPixels;
        }

        int height= imageView.getHeight();//获取实际宽度

        if(height<=0){
            height=lp.height;//获取imageview在layout声明的宽度
        }
        if(height<=0){
        //  height=imageView.getMaxHeight();//检查最大值
            height= getImageValue(imageView, "mMaxHeight");
        }
        if(height<=0){
            height=dm.heightPixels;
        }

        imageSize.width=width;
        imageSize.height=height;
        return imageSize;           
    }
    //通过反射获取imageView的某属性值
    private static int getImageValue(Object object,String name){
        int value=0;
        try {
            Field field=ImageView.class.getDeclaredField(name);
            field.setAccessible(true);
            try {
                int fieldValue=field.getInt(object);
                if(fieldValue>0 && fieldValue<Integer.MAX_VALUE){
                    value=fieldValue;
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return value;

    }

    private synchronized void addTasks(Runnable runnable) {
        mTaskQueue.add(runnable);
        try {//防止用到mPoolThreadHandler的时候还没初始化,如何执行到此的时候,还为空,会请求一个。这样就不回空指针了
            if(mPoolThreadHandler==null)
            sePoolTH.acquire();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        mPoolThreadHandler.sendEmptyMessage(0x110);

    }
    private Bitmap getBitmapFromLruCache(String key) {

        return mLruCache.get(key);
    }
    //这个类防止加载的图片错乱,用一个对象统一。
    private class ImgBeanHolder{
        Bitmap bitmap;
        ImageView imageView;
        String path;        
    }

    private class ImageSize{
        int width;
        int height;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值