Android用kotlin修改一个图片编辑库做一个作业点评的功能

Android 原生实现图文混排编辑器 Android 原生控件实现图文混排、混编,支持编辑网络图片和本地图片,超好用。 立即下载

准备工作

  • github 找一个图片编辑器 地址,这个图就是对单个本地图片进行编辑,加载大图有问题,页面传递bitmap有问题
  • 目前已有功能:编辑一张本地图片
  • 想要效果:传入图片下载list,第一个页面要有图片切换效果,点击按钮对图片进行编辑,图片保存

优化

  • 首页直接glide加载图片,viewpager左右切换,对bitmap压缩防止过大导致页面崩溃
  • 点击进入编辑页的时候,对对应的图片进行下载,压缩,之后再编辑,跳转页面的时候传下载压缩后的路径,而不是传递bitmap
  • 因为编辑的时候是对本地图编辑,会有图片下载,保证本地相册不会过多下载图片

代码

首页传入图片集合,进行展示,对图片编辑需要申请权限

package me.minetsh.imaging

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri

import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter
import android.support.v4.view.PagerAdapter
import android.support.v4.view.ViewPager
import android.support.v7.app.AppCompatActivity

import android.view.View
import android.view.ViewGroup
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.htjy.app.common_util.ext.clickWithTrigger
import com.htjy.app.common_util.util.LuBanUtils

import com.tbruyelle.rxpermissions2.RxPermissions
import kotlinx.android.synthetic.main.image_activity_img_handle.*
import kotlinx.android.synthetic.main.image_edit_ready.*


/**
Func:点评的首页
# 老师点评图片不可超过9张,超过时不可再点击”红笔点评“按钮,点击提示:“点评图片不可超过9张”
# 点击老师点评的图片可继续点评,保存后替换原点评图片
# 点评跳转的是新页面-仿微信
# 点评不能左右翻动
# 点评的时候不传递bitmap,而是传递路径
 */
const val EDIT_IMG_REQUESTCODE = 1000

class IMGHandleActivity : AppCompatActivity() {

    private lateinit var dataList: ArrayList<String>  //数据源
    private var type: Int = 0   //0 是网络  1是本地

    //测试的网络数据
    private val testUrl = arrayListOf("https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAL-USZ-F0(0)011.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAM-USZ-F0(0)012.png"
            , "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAA-USZ-F0(0)001.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAAB-USZ-F0(0)002.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MAEC-USZ-F0(0)099.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL-C-MACD-USZ-F0(0)052.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MAGY_USZlow_167_R_ad.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MAHB_USZlow_170_R_ad.png",
            "https://animal-crossing.com/amiibo/assets/img/cards/NVL_C_MALZ_USZlow_264_R_ad.png")
    //测试的本地数据
    private var test = arrayListOf(
            "/storage/emulated/0/DCIM/Screenshots/Screenshot_2019-10-30-10-09-35-727_com.htjy.gardener.component_remark_teacher.png", "/storage/emulated/0/BesttimerCamera2/picture_1574761262840.jpg")


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.image_activity_img_handle)
        //EventBus.getDefault().register(IMGHandleActivity@ this)
        initData()
        //initView()
    }


    override fun onDestroy() {
        super.onDestroy()
        //EventBus.getDefault().unregister(IMGHandleActivity@ this)
    }


    companion object {
        fun launch(from: Activity, data: ArrayList<String>, type: Int) {
            val intent = Intent(from, IMGHandleActivity::class.java)
            intent.putExtra("type", type)
            intent.putStringArrayListExtra("data", data)
            from.startActivityForResult(intent, EDIT_IMG_REQUESTCODE)
        }
    }


    private fun initData() {
        //dataList = testUrl
        dataList = intent.getStringArrayListExtra("data")
        type = intent.getIntExtra("type", 0)
        //申请读写权限
        //Singleton().test(arrayListOf("1","2","3"))
        RxPermissions(IMGHandleActivity@ this)
                .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
                .subscribe {
                    if (it) {
                        initView()
                    } else {
                        finish()
                    }
                }


    }


   

    private var currentPage = 0
    private val fragmentList = arrayListOf<ItemFragment>()
    private fun initView() {
        //获取Bitmap
        //val layoutInflater = LayoutInflater.from(IMGHandleActivity@ this)

        for (value in dataList) {
            fragmentList.add(ItemFragment.launch(value))
        }

        val adapter = MyItemFragmentPagerAdapter(supportFragmentManager, fragmentList)
        vp_images.adapter = adapter
        vp_images.offscreenPageLimit = dataList.size
        tv_count.text = "${currentPage + 1}/${dataList.size}"
        vp_images.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {

            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {

            }

            override fun onPageSelected(position: Int) {
                currentPage = position
                tv_count.text = "${currentPage + 1}/${dataList.size}"
            }
        })

        /**
         * 点评:如果是网络图片必须要先下载才能进入点评,步骤为:下载,copy,压缩
         */
        btn_dianping.clickWithTrigger {

            when (type) {
                0 -> {
                    LuBanUtils.loadPicFromNet(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
                        if (isSuccess) {
                            IMGEditActivity.launch(path, this@IMGHandleActivity)
                        }
                    }
                }
                1 -> {
                    LuBanUtils.loadPicFromLocal(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
                        if (isSuccess) {
                            IMGEditActivity.launch(path, this@IMGHandleActivity)
                        }
                    }
                }
            }

        }

        btn_save.clickWithTrigger {
            //保存
            when (type) {
                0 -> {
                    LuBanUtils.downloadPicFromNet(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
                        if (isSuccess) {
                            // 最后通知图库更新
                            sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://$path")))
                            ToastUtils.showShort("图片保存成功")
                        } else {
                            ToastUtils.showShort("图片保存失败")
                        }
                    }
                }
//                1 -> {
//                    LuBanUtils.loadPicFromLocal(dataList[currentPage], IMGHandleActivity@ this) { isSuccess, path ->
//                        if (isSuccess) {
//                            IMGEditActivity.launch(path, this@IMGHandleActivity)
//                        }
//                    }
//                }
            }

        }
    }


    private inner class MyItemFragmentPagerAdapter(fm: FragmentManager, var data: List<Fragment>) : FragmentPagerAdapter(fm) {
        override fun getItem(position: Int): Fragment {
            return data[position]
        }

        override fun getCount(): Int {
            return data.size
        }

    }


    private inner class MyImgViewPagerAdapter(var context: Context, var data: List<View>) : PagerAdapter() {
        override fun isViewFromObject(view: View, obj: Any): Boolean {
            return view == obj
        }

        override fun getCount(): Int {
            return data.size
        }

        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
            container.removeView(data[position])
        }

        override fun instantiateItem(container: ViewGroup, position: Int): Any {
            container.addView(data[position])
            return data[position]
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == EDIT_IMG_REQUESTCODE && resultCode == Activity.RESULT_OK) {
            //编辑图片成功的返回
            data?.let {
                val savePath = it.getStringExtra(IMGEditActivity.EXTRA_IMAGE_SAVE_PATH)
                LogUtils.d("保存图片的绝对路径==上级页面收到>>>$savePath")
                setResult(EDIT_IMG_REQUESTCODE, it)
                finish()
            }
        }
    }
}

图片编辑页的修改

package me.minetsh.imaging;

import android.app.Activity;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.text.TextUtils;

import com.blankj.utilcode.util.LogUtils;


import me.minetsh.imaging.core.IMGMode;
import me.minetsh.imaging.core.IMGText;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;

import static me.minetsh.imaging.IMGHandleActivityKt.EDIT_IMG_REQUESTCODE;

public class IMGEditActivity extends IMGEditBaseActivity {

    private static final int MAX_WIDTH = 1024;

    private static final int MAX_HEIGHT = 1024;

    public static final String EXTRA_IMAGE_URI = "IMAGE_URI";

    public static final String EXTRA_IMAGE_SAVE_PATH = "IMAGE_SAVE_PATH";


    public static void launch(String url, Activity fromContext) {
        Intent intent = new Intent(fromContext, IMGEditActivity.class);
        intent.putExtra("bitmap", url);
        //默认一个编辑图片后的路径
        intent.putExtra(EXTRA_IMAGE_SAVE_PATH, UUID.randomUUID().toString() + ".jpg");
        fromContext.startActivityForResult(intent, EDIT_IMG_REQUESTCODE);
    }

    @Override
    public void onCreated() {

    }

    private File currentFile;

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        if (currentFile != null && currentFile.exists()) {
            currentFile.delete();
        }
    }

    @Override
    public Bitmap getBitmap() {
        String url = getIntent().getStringExtra("bitmap");
        // String localPath = LuBanUtils.INSTANCE.getPicMap().get(url);
        currentFile = new File(url);
        Bitmap bitmap = BitmapFactory.decodeFile(currentFile.getPath());


        // Bitmap bitmap = getIntent().getParcelableExtra("bitmap");

//        Intent intent = getIntent();
//        if (intent == null) {
//            return null;
//        }
//
//        Uri uri = intent.getParcelableExtra(EXTRA_IMAGE_URI);
//        if (uri == null) {
//            return null;
//        }
//
//        IMGDecoder decoder = null;
//
//        String path = uri.getPath();
//        if (!TextUtils.isEmpty(path)) {
//            switch (uri.getScheme()) {
//                case "asset":
//                    decoder = new IMGAssetFileDecoder(this, uri);
//                    break;
//                case "file":
//                    decoder = new IMGFileDecoder(uri);
//                    break;
//            }
//        }
//
//        if (decoder == null) {
//            return null;
//        }
//
//        BitmapFactory.Options options = new BitmapFactory.Options();
//        options.inSampleSize = 1;
//        options.inJustDecodeBounds = true;
//
//        decoder.decode(options);
//
//        if (options.outWidth > MAX_WIDTH) {
//            options.inSampleSize = IMGUtils.inSampleSize(Math.round(1f * options.outWidth / MAX_WIDTH));
//        }
//
//        if (options.outHeight > MAX_HEIGHT) {
//            options.inSampleSize = Math.max(options.inSampleSize,
//                    IMGUtils.inSampleSize(Math.round(1f * options.outHeight / MAX_HEIGHT)));
//        }
//
//        options.inJustDecodeBounds = false;
//
//        Bitmap bitmap = decoder.decode(options);
//        if (bitmap == null) {
//            return null;
//        }

        return bitmap;
    }

    @Override
    public void onText(IMGText text) {
        mImgView.addStickerText(text);
    }

    @Override
    public void onModeClick(IMGMode mode) {
        IMGMode cm = mImgView.getMode();
        if (cm == mode) {
            mode = IMGMode.NONE;
        }
        mImgView.setMode(mode);
        updateModeUI();

        if (mode == IMGMode.CLIP) {
            setOpDisplay(OP_CLIP);
        }
    }

    @Override
    public void onUndoClick() {
        IMGMode mode = mImgView.getMode();
        if (mode == IMGMode.DOODLE) {
            mImgView.undoDoodle();
        } else if (mode == IMGMode.MOSAIC) {
            mImgView.undoMosaic();
        }
    }

    @Override
    public void onCancelClick() {
        onBackPressed();
    }

    @Override
    public void onDoneClick() {

        File file = new File(getCacheDir(), getIntent().getStringExtra(EXTRA_IMAGE_SAVE_PATH));
        String path = file.getAbsolutePath();
        if (!TextUtils.isEmpty(path)) {
            Bitmap bitmap = mImgView.saveBitmap();
            if (bitmap != null) {
                FileOutputStream fout = null;
                try {
                    fout = new FileOutputStream(path);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fout);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    if (fout != null) {
                        try {
                            fout.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                Intent intent = new Intent();
                //把保存的图片的绝对路径返回出去
                intent.putExtra(EXTRA_IMAGE_SAVE_PATH, path);
                LogUtils.d("保存图片的绝对路径>>>" + path);
                setResult(RESULT_OK, intent);
                finish();
                return;
            }
        }
        setResult(RESULT_CANCELED);
        finish();
    }

    @Override
    public void onCancelClipClick() {
        mImgView.cancelClip();
        setOpDisplay(mImgView.getMode() == IMGMode.CLIP ? OP_CLIP : OP_NORMAL);
    }

    @Override
    public void onDoneClipClick() {
        mImgView.doClip();
        setOpDisplay(mImgView.getMode() == IMGMode.CLIP ? OP_CLIP : OP_NORMAL);
    }

    @Override
    public void onResetClipClick() {
        mImgView.resetClip();
    }

    @Override
    public void onRotateClipClick() {
        mImgView.doRotate();
    }

    @Override
    public void onColorChanged(int checkedColor) {
        mImgView.setPenColor(checkedColor);
    }
}

图片下载压缩,过多文件删除的工具类

package com.htjy.app.common_util.util


import android.content.Context
import android.os.Environment
import com.blankj.utilcode.util.LogUtils
import com.htjy.baselibrary.widget.imageloader.ImageLoaderUtil
import com.htjy.baselibrary.widget.imageloader.listener.ImageDownloadListener
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException


/**
 * 鲁班压缩工具类
 */
object LuBanUtils {
 

  

    /**
     * 复制文件
     *
     * @param source 输入文件
     * @param target 输出文件
     */
    private fun copy(source: File?, callBack: (pathFile: File) -> Unit) {
        //获取到下载得到的图片,进行本地保存
        //获取到下载得到的图片,进行本地保存
        val pictureFolder: File = Environment.getExternalStorageDirectory()
        //第二个参数为你想要保存的目录名称
        //第二个参数为你想要保存的目录名称
        val appDir = File(pictureFolder, "my_edit_pic")
        if (!appDir.exists()) {
            appDir.mkdirs()
        }
        val fileName = System.currentTimeMillis().toString() + ".jpg"
        val destFile = File(appDir, fileName)
        var fileInputStream: FileInputStream? = null
        var fileOutputStream: FileOutputStream? = null
        try {
            fileInputStream = FileInputStream(source)
            fileOutputStream = FileOutputStream(destFile)
            val buffer = ByteArray(1024)
            while (fileInputStream.read(buffer) > 0) {
                fileOutputStream.write(buffer)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            try {
                fileInputStream?.close()
                fileOutputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
            //删掉源文件
            if (source != null && source.exists()) {
                source.delete()
            }
            callBack.invoke(destFile)
        }
    }


 
   private  fun deleteFile(file: File?) {
        if (file != null && file.exists()) {
            file.delete()
        }
    }

    /**
     * 下载图片 copy  压缩
     */
    fun loadPicFromNet(uri: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
        Flowable.just(uri).subscribeOn(Schedulers.io()).map {
            //下载图片
            ImageLoaderUtil.getInstance().downloadOnly(context, uri, object : ImageDownloadListener {
                override fun onSuccess(path: String?) {
                    LogUtils.d("鲁班>>>Gilde下载图片成功==${uri}")
                    LogUtils.d("鲁班>>>Gildecopy图片")
                    path?.let {
                        val file = File(it)
                        if (file != null && file.exists()) {
                            //这里只是缓存文件,还需要copy成正式文件
                            copy(file) { targetFile ->
                                LogUtils.d("鲁班==copy成功===${targetFile.absolutePath}")
                                LogUtils.d("鲁班>>>压缩图片")
                                Luban.with(context)
                                        .load(targetFile)
                                        .setTargetDir(targetFile.parent)
                                        .ignoreBy(10)
                                        .setCompressListener(object : OnCompressListener {
                                            override fun onSuccess(file: File?) {
                                                LogUtils.d("鲁班>>>压缩成功==${uri}===${file?.absolutePath}")
                                                //删掉copy的文件
                                                deleteFile(targetFile)
                                                callBack.invoke(true, file?.absolutePath)
                                            }

                                            override fun onError(e: Throwable?) {
                                                LogUtils.d("鲁班>>>压缩失败==${uri}===${e?.message}")
                                                callBack.invoke(false, null)
                                            }

                                            override fun onStart() {
                                                LogUtils.d("鲁班>>>开始压缩==${uri}===")
                                            }

                                        })
                                        .launch()
                            }
                        }
                    }

                }

                override fun onFail() {
                    LogUtils.d("鲁班>>>Gilde下载图片失败==${uri}")
                    callBack.invoke(false, null)
                }
            })
        }.subscribe()
    }

    /**
     * 加载本地图片并压缩
     */
    fun loadPicFromLocal(path: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
        //对本地图做一定的压缩就行了
        Flowable.just(path).subscribeOn(Schedulers.io()).map { uri ->
            val file = File(uri)
            LogUtils.d("鲁班>>>开始压缩==${uri}")
            if (file != null && file.exists()) {
                Luban.with(context)
                        .load(file)
                        .setCompressListener(object : OnCompressListener {
                            override fun onSuccess(file: File?) {
                                if (file != null && file.exists()) {
                                    LogUtils.d("鲁班>>>压缩成功==${uri}===${file?.absolutePath}")
                                    callBack.invoke(true, file?.absolutePath)
                                    //target.onNext("")
                                }
                            }

                            override fun onError(e: Throwable?) {
                                LogUtils.d("鲁班>>>压缩失败==${uri}===${e?.message}")
                                callBack.invoke(false, null)
                            }

                            override fun onStart() {
                                LogUtils.d("鲁班>>>开始压缩==${uri}===")
                            }

                        })
                        .launch()
            }
        }.subscribe()

    }


    /**
     * 下载线上的图片
     */
    fun downloadPicFromNet(uri: String, context: Context, callBack: (isSuccess: Boolean, path: String?) -> Unit) {
        Flowable.just(uri).subscribeOn(Schedulers.io()).map {
            //下载图片
            ImageLoaderUtil.getInstance().downloadOnly(context, uri, object : ImageDownloadListener {
                override fun onSuccess(path: String?) {
                    LogUtils.d("鲁班>>>Gilde下载图片成功==${uri}")
                    LogUtils.d("鲁班>>>Gildecopy图片")
                    path?.let {
                        val file = File(it)
                        if (file != null && file.exists()) {
                            //这里只是缓存文件,还需要copy成正式文件
                            copy(file) { targetFile ->
                                LogUtils.d("鲁班==下载成功===${targetFile.absolutePath}")
                                callBack.invoke(true, targetFile.absolutePath)
                            }
                        }
                    }

                }

                override fun onFail() {
                    LogUtils.d("鲁班>>>Gilde下载图片失败==${uri}")
                    callBack.invoke(false, null)
                }
            })
        }.subscribe()
    }
}

分享一个下载图片,并刷新手机相册的工具类

二进制bytearr转bitmap

    fun getBitmapFromByte(temp: ByteArray): Bitmap? {
        return if (temp != null) {
            BitmapFactory.decodeByteArray(temp, 0, temp.size)
        } else {
            null
        }
    }

下载图片保存,刷新相册

/**
     * 保存图片到图库
     *
     * @param context
     * @param bmp
     */
    public static void saveImageToGallery(Context context, Bitmap bmp, String picDirName) {
        // 首先保存图片
//        File appDir = new File(Environment.getExternalStorageDirectory(),
//                picDirName);
        File appDir = new File(getFilePath(context,picDirName));
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        LogUtils.d("下载啊目录>>"+appDir.getAbsolutePath());
        String fileName = System.currentTimeMillis() + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {

            ToastUtils.showShort("保存失败");
            e.printStackTrace();
        } catch (IOException e) {
            ToastUtils.showShort("保存失败");
            e.printStackTrace();
        }

        // 其次把文件插入到系统图库
        try {
            MediaStore.Images.Media.insertImage(context.getContentResolver(),
                    file.getAbsolutePath(), fileName, null);
            ToastUtils.showShort("保存成功");
        } catch (FileNotFoundException e) {
            ToastUtils.showShort("保存失败");
            e.printStackTrace();
        }
        // 最后通知图库更新
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
                Uri.fromFile(file)));
    }


    public static String getFilePath(Context context, String dir) {
        String directoryPath = "";
        //判断SD卡是否可用
        if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            directoryPath = context.getExternalFilesDir(dir).getAbsolutePath();
            // directoryPath =context.getExternalCacheDir().getAbsolutePath() ;
        } else {
            //没内存卡就存机身内存
            directoryPath = context.getFilesDir() + File.separator + dir;
            // directoryPath=context.getCacheDir()+File.separator+dir;
        }
        File file = new File(directoryPath);
        if (!file.exists()) {//判断文件目录是否存在
            file.mkdirs();
        }
        LogUtil.i("filePath====>" + directoryPath);
        return directoryPath;
    }
中国1990-2021连续30年土地利用CLCD数据分享 CLCD(China Land Cover Dataset)数据集是由武汉大学黄昕老师基于Google Earth Engine上的335,709景Landsat数据制作的中国年度土地覆盖数据集。为了提高CLCD的时空一致性,还提出了一种包含时空滤波和逻辑推理的后处理方法。该数据集最大的优势在于每年30米的土地利用分类结果,且连续覆盖了30年时间。相比其他产品如GLC_FCS30、Global30、AGLC2000_2015、FROM-GLC10、ESA10、ESRI10等,CLCD在时间分辨率上更高。 阅读详情

相关推荐

DCA1000EVM数据采集实战:从mmWave Studio配置到MATLAB后处理的全链路解析

本文详细解析了DCA1000EVM数据采集的全流程,从mmWave Studio配置到MATLAB后处理,涵盖数据存储结构解析、后处理引擎剖析及Python数据处理方案。通过实战案例,帮助开发者高效处理毫米波雷达原始数据,实现从数据到应用价值的转化,提升开发效率。

weixin_33743661的博客 706

【免费下载】 推荐一款强大的Android图片编辑开源库 - PhotoEdit

在数字世界中,图像编辑已经成为日常生活的一部分,无论是社交媒体的个人头像还是商业宣传材料,都需要高质量的图片来吸引注意力。今天,我们很高兴向您推荐一个专为Android平台设计的开源图片编辑库 - PhotoEdit。这个项目提供了丰富的功能,帮助开发者轻松实现专业的图片处理效果。 ## 1. 项目介绍 PhotoEdit 是一个完全免费且易于集成的Android项目,由Java和C++代码编...

gitblog_00065的博客 1969

TPC好像是这么回事儿

TPC(Transmit Power Control)发送功率控制,本觉得就是控制一下发送功率,原理很简单,但看原版协议死活每搞懂什么意思,恼羞成怒!!!! 后来又看了一些贴子和cisco的相关功能介绍,再回头看协议,好像是这么一回事: 1、所谓TPC,是AP控制STA的发送功率的一种手段。在802.11d中,已定义了使用Country Element传递区域无线通信规定的信道和最大发送功率信息: 通过获取这些信息,STA可以确定自己应该遵守什么规则,可以用什么发送功率在哪些信道上通信。 2

两只勤劳的蜜蜂 8625

推荐项目:PhotoEditor——打造属于你的图片编辑神器

在当今这个视觉主导的时代,一款强大且易用的图片编辑工具几乎成为每个人的必备。今天,我们要向大家隆重推荐一个开源宝藏——**PhotoEditor**。这是一个专为Android开发者设计的图片编辑库,它让你轻松集成画图、添加文本、滤镜效果、表情符号和贴纸等功能于你的应用中,仿佛随身携带了一个小型的专业图像工作室。 ## 项目技术分析 PhotoEditor采用Kotlin编写的最新版本,确保了...

gitblog_00406的博客 547

极简JEPA世界模型实战:低门槛复现LeWorldModel,CPU可跑

这次我们来看一个能让普通开发者真正上手跑起来的“世界模型”项目。如果你对Yann LeCun提出的JEPA(联合嵌入预测架构)框架感兴趣,但又苦于Meta官方实现过于庞大复杂,那么这个GitHub上4k star的项目就是为你准备的。它用极简的PyTorch代码(核心文件仅160-278行)完整复刻了包括LeWorldModel在内的五个JEPA核心变体,最大的亮点是: 门槛极低,普通笔记本甚至CPU就能跑,显存占用友好,非常适合学习和原理验证。 这个项目名为 keon/jepa ,完全开源。它剥离了工业级

weixin_29031161的博客 421

CST微波工作室端口设置保姆级教程:从波导端口到离散端口,手把手教你搞定S参数仿真

本文提供CST微波工作室端口设置的详细教程,涵盖波导端口和离散端口的选择与配置,助力工程师精准完成S参数仿真。通过微带线滤波器案例,详解端口尺寸、阻抗设置及常见问题排查,提升仿真效率与准确性。

weixin_42570296的博客 360

Android开发常用开源框架:图片处理

1、图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 Fresco 一个用于管理图像和他们使用的内存的库 Glide 一个图片加载和缓存的库,使用的App有:网易新闻 GlidePalette Androi

JackChan 3万+

zblog mysql数据库无法连接_ZBlog博客网站错误:MySQL数据库无法连接

前言前天搭建的博客网站,睡醒一觉起来网站挂逼了,其间无任何操作。主页都进不去,提示“MySQL数据库无法连接”,无论怎么刷新重启服务器都没用。最终从中午1点搞到凌晨2点,皇天不负有心人终于解决了。其实问题很简单,只是没这方面的经验,走了很多弯路。我本人并不是搞WEB的,搞这个博客纯碎就是玩玩罢了。处理这个问题的时候查了很久的资料,都是一些没太大意义的,无非就是让你改zb_user/c_option...

weixin_34691461的博客 2204

调用系统相机和相册以及裁剪实现图片的上传

//清单文件中的权限  &lt;uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /&gt;     &lt;uses-permission android:name="android.permission.CAMERA" /&gt;     &lt;uses-permission androi...

weixin_41649874的博客 349

PX4(CUAV v6x 飞控)+ QAV250 机架无人机组装测试教学(无人机电机转向不对,如何反转电机?)

本文介绍无人机的组装与飞控调试:1.无人机的硬件组装。2.无人机飞控软件的下载,电机、遥控器、电调的校准与调试。

weixin_74185504的博客 2889

解决Android API 30+视频播放抖动问题

摘要:本文探讨了Android开发中使用ExoPlayer在API 30+设备上出现的视频抖动问题。作者发现API 30以下版本运行正常,但API 30及以上版本会出现画面不稳定现象。解决方案是改用TextureView替代SurfaceView,通过修改PlayerView配置并设置setUseTextureView(true)来增强播放稳定性。这一简单改动有效解决了API兼容性问题,为开发者提供了实用的解决方案。

m0_62153576的博客 114

android 工具 拓扑图,GitHub - AndroidHelper/graph.editor: HTML5拓扑图编辑器

#拓扑图编辑器介绍Graph.Editor是一款基于HTML5技术的拓补图编辑器,采用jquery插件的形式,是Qunee图形组件的扩展项目数据可视化是一件有趣的工作,冰冷的数据变成美观的图形,如同命令行之于Mac OS X图形界面的,传统的图形组件以图表(Chart)居多,不少优秀的开源或者商业的作品,比如d3js, echarts, highcharts,而拓扑图、流程图组件相对少一些,知名的...

weixin_31589747的博客 1147

【零基础入门unity游戏开发——2D篇】大大简化了地形创建流程,减少Rule Tile的使用 —— AutoTile自动平铺瓦片的使用介绍

Unity 2D游戏开发中的AutoTile技术详解 摘要:本文详细介绍了Unity 2D游戏开发中的AutoTile技术,这是一种智能瓦片系统,能够自动处理地形和场景拼接。文章首先讲解了AutoTile的基本概念和优势,然后分步骤指导读者如何安装2D Tilemap Extras包、创建AutoTile、设置参数和绘制遮罩。通过实际案例演示了AutoTile的使用方法,并比较了AutoTile与Rule Tile的适用场景。最后指出AutoTile虽然使用方便,但对素材要求较高,开发者应根据项目需求选择合

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 727

推荐开源项目:Simple Android Editor - 手持视频与图片修复神器

推荐开源项目:Simple Android Editor - 手持视频与图片修复神器 在快速发展的移动应用领域中,有一款开源工具因其独特的功能而脱颖而出——Simple Android Editor。这不仅仅是一款基础的视频编辑器,它融合了深度学习的力量,让视频与照片修复工作变得简单直观。今天,让我们一起探索这款应用的魅力所在,并揭秘它如何能成为你创意工作的得力助手。 项目介绍 Simple An...

gitblog_00018的博客 843

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android(安卓) 选择或者拍摄图片,可设置图片数量(kotlin)

格式配置权限导入依赖选择图片 你好! 配置权限 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 导入依赖 //图片选择 implementation 'io.github.lucksiege:picturesel

chaseDreamer_的博客 2517

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【亲测免费】 推荐一款强大的Android富文本编辑器:RichEditorView

推荐一款强大的Android富文本编辑器:RichEditorView 在移动应用开发中,有时候我们需要为用户提供一个功能齐全、可自定义的文本编辑界面。对于Android开发者来说,找到这样的组件并不总是易如反掌。今天,我想向大家推荐一款开源项目——,它是一个高度定制化的Android富文本编辑器,能够满足你的各种需求。 项目简介 RichEditorView是由开发者YouLookWhat创建并...

gitblog_00057的博客 1094

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Android开源项目以及开源框架,各种UI实现效果。

开源项目和开源框架第一期 图片 Android-Universal-Image-Loader★15152 - 异步图像加载程序 glide★15006 - 媒体管理和图片加载框架 picasso★13268 - 安卓图片缓存库 fresco★12543 - 在Android应用中显示图片 PhotoView★9843 - 简单可用的放大安卓ImageView

lixuce1234的博客 3395

推荐开源项目:StickerView - 打造你的个性化图片编辑利器!

推荐开源项目:StickerView - 打造你的个性化图片编辑利器! 1、项目介绍 StickerView 是一个专为 Android 设计的图片编辑库,它允许用户在图像上添加贴纸和文字,并进行一系列高级编辑操作,如镜像翻转、元素置顶、自由缩放和移动等。该项目由 nimengbo 开发并开源,提供了一个简单易用的界面,让开发者能够轻松集成到自己的应用中,让用户享受趣味十足的照片编辑体验。 2、项...

gitblog_00041的博客 630

Android Kotlin 打开相册选择图片(多选)

【代码】Android Kotlin 打开相册选择图片(多选)

ZHOUYONGXYZ的专栏 1420

图像编辑器 Monica 之各种图形绘制、图像调色

一. 图像编辑器 MonicaMonica 是一款跨平台的桌面图像编辑软件(早期主要是个人为了验证一些算法而产生的)。screenshot.png其技术栈如下:Kotlin 编写 UI(Kotlin Compose Desktop 作为 UI 框架)基于 mvvm 模式,依赖注入使用 koin,编译使用 JDK 17。部分算法使用 Kotlin 实现。其余的算法使用 OpenCV C++ 来实现,...

Java与Android技术栈 1547

android 图片上编辑器,Android中的图片编辑器

检查一下:您可以使用以下方法在位图上设置对比度:private static void setContrastScaleOnly(ColorMatrix cm, float contrast) {float scale = contrast + 1.f;float translate = (-.5f * scale + .5f) * 255.f;cm.set(new float[] {scale,...

weixin_39633781的博客 139

Android实现多图上传工具类

来个用okhttp3实现的图片上传,方便以后使用 import android.content.Context; import android.graphics.Bitmap; import android.net.Uri; import android.provider.MediaStore; import com.google.gson.reflect.TypeToken; impor...

zd_1471278687的专栏 867

Photo Editor Android 开源项目教程

Photo Editor Android 是一个功能强大的开源图片编辑器项目,适用于 Android 平台。该项目提供了丰富的图片编辑功能,包括裁剪、旋转、滤镜、文字添加等。它不仅适用于个人开发者,也适合企业级应用开发。 ## 项目快速启动 ### 环境准备 在开始之前,请确保你已经安装了以下工具和环境: - Android Studio - Java JDK - Git ### 克隆项...

gitblog_00204的博客 648

Kotlin上传相册图片到服务器

打开图库拿到图片 /** * 相册 */ private fun takeGallery() { val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) intent.type = "image/*" startActivityForResult(intent, REQUEST_CODE_PICK_PHO.

jingerlovexiaojie的博客 1744

android c编辑器,Android 图文编辑器

原先项目用的github上的一个richtext图文编辑器,但是不太符合我的输入习惯,因此改进了一下,只支持简单的文字和图片输入,其他功能可以自己加,代码如下:Glide.with(this).asBitmap().load(it).into(object : SimpleTarget() {override fun onResourceReady(resource: Bitmap, transi...

weixin_35998791的博客 325

android 仿qq 图片编辑器,GitHub - iDan1230/ImageEditor: 仿QQ图片编辑器,根据手势在图片上画矩形,圆,箭头,铅笔,擦除功能...

ImageEditor仿QQ图片编辑器,根据手势在图片上画矩形,圆,箭头,铅笔,擦除功能新的项目中需要做一个类似QQ截图随手势编辑图片的功能1.绘制矩形框2.绘制圆3.绘制箭头4.铅笔画线5.文字输入6.可以撤回上一个绘制的path7.用法:代码中直接newsurfce = new CustomSurfaceView(this, url, false);传入不同参数实现绘制case R.id.bi...

weixin_33662445的博客 250
上一篇: 读《实战gradle》感悟
下一篇: flutter 学习感悟
泡温泉的咸鱼
博客等级 码龄11年 39粉丝 48原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值