圖片選擇器ImageEditContainer

来源:http://www.cnblogs.com/dingzq/archive/2017/07/07/7130317.html
-Advertisement-
Play Games

1. 簡介 本次demo中一共封裝了兩個組件:ImageEditButton 和 ImageEditContainer。其中ImageEditContainer 是在 ImageEditButton,兩個組件可單獨使用。 在demo中,實現了 圖片選擇(拍照+本地),裁剪,壓縮,保存本地 以及對已選 ...


 

1. 簡介

本次demo中一共封裝了兩個組件:ImageEditButton 和 ImageEditContainer。其中ImageEditContainer 是在 ImageEditButton,兩個組件可單獨使用。

在demo中,實現了 圖片選擇(拍照+本地),裁剪,壓縮,保存本地 以及對已選擇圖片的刪除操作(如果有修改需求,也可以使用對應方法進行操作,該方法已添加);

 還有就是 針對 6.0許可權的處理問題,本次使用了第三方庫 rxpermissions 進行許可權的處理。

2.項目主目錄結構

 

3. 功能介紹

MainActivity.java 界面效果圖:

 

 

ImageEditContainer 組件初始化:
        layImageContainer = (ImageEditContainer) findViewById(R.id.lay_image_container);
        layImageContainer.setEditListener(this);
        layImageContainer.setBtnImageResource(R.drawable.icon_picture_photograph);
        layImageContainer.setTotalImageQuantity(3);

如上代碼,設置組件的監聽,添加按鈕展示圖,以及最多選擇圖片個數。

implements  ImageEditContainer.ImageEditContainerListener 的實現
@Override
    public void doAddImage() {
        PopupWindow mCameraPop = SelectPicturePopupWindowUtils.showSelectPicturePopupWindow(this);
        if (mCameraPop != null)
            mCameraPop.showAtLocation(layImageContainer, Gravity.BOTTOM, 0, 0);
    }

    @Override
    public void doEditLocalImage(ImageItem imageItem) {
        if (imageItem != null) {
            layImageContainer.updateEditedImageItem(imageItem);
        }
    }

    @Override
    public void doEditRemoteImage(RemoteImageItem remoteImageItem) {
        if (remoteImageItem != null) {
            if (remoteImageItem.isDeleted) {
                layImageContainer.removeRemoteImageItem(remoteImageItem);
            } else {
                layImageContainer.updateRemoteImageItem(remoteImageItem);
            }
        }
    }

  

當圖片選擇數量達到最大個數時,添加按鈕會消失。效果圖如下所示:

 

圖片裁剪 效果圖如下所示:

圖片可拖拽,縮放


圖片選擇好後,進行圖片壓縮:
    private void compressImage(String path) {

        if (TextUtils.isEmpty(path)) {
            return;
        }
        compressImage = compressImage + 1;
        ImageItem imageItem = new ImageItem();
        imageItem.storedPath = path;

        File file = new File(FilePathUtils.getImageSavePath());
        if (!file.exists()) {
            file.mkdirs();
        }
        String filePath = FilePathUtils.getImageSavePath() + System.currentTimeMillis() + ".jpg";
        new Thread(new MyThread(imageItem, path, filePath)).start();
        List<String> imagePaths = new ArrayList<>();
        imagePaths.add(path);
        layImageContainer.addNewImageItem(imageItem);
    }

圖片壓縮比較慢,要開啟個 線程進行壓縮:

public class MyThread implements Runnable {
        private String imgPath;
        private String outPath;
        private ImageItem imageItem;

        public MyThread(ImageItem imageItem, String imgPath, String outPath) {
            this.imageItem = imageItem;
            this.imgPath = imgPath;
            this.outPath = outPath;
        }

        public void run() {
            try {
                BitmapUtil.compressAndGenImage(imgPath, outPath, 500, false);
                compressImage = compressImage - 1;
                imageItem.storedPath = outPath;
            } catch (IOException e) {
                compressImage = compressImage - 1;
                e.printStackTrace();
            }
        }
    }

使用的壓縮方法:

    /**
     * Compress by quality,  and generate image to the path specified
     *
     * @param imgPath
     * @param outPath
     * @param maxSize     target will be compressed to be smaller than this size.(kb)
     * @param needsDelete Whether delete original file after compress
     * @throws IOException
     */
    public static void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {
        compressAndGenImage(getBitmap(imgPath), outPath, maxSize);

        // Delete original file
        if (needsDelete) {
            File file = new File(imgPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }

 

組件 ImageEditContainer 添加圖片方法介紹:

可添加本地和網路圖片

    /**
     * 添加本地圖片 
     * List<String> storePaths 本地圖片路徑數組
     */
    public void addNewImages(List<String> storePaths) {


    }


    /**
     * 添加本地圖片
     */
    public void addNewImageItem(ImageItem imageItem) {

    }

    /**
     * 添加網路圖片
     */
    public void addRemoteImageItem(RemoteImageItem remoteImageItem) {

    }

 

組件 ImageEditContainer 其他方法介紹:

   /**
     * 設置組件中 選擇按鈕的寬高
     */
public void setImvHeightAndWidth(int height, int width) {

    }
  /**
     * 設置圖片最大數量
     */
    public void setTotalImageQuantity(int totalImageQuantity) {

    }
/**
     * 設置圖片展示圖
     */
    public void setBtnImageResource(int resid) {

    }
  /**
     * 獲取組件中所有圖片對象(本地+網路)
     */
    public List<Object> getAllImageItems() {

    }

  public void updateEditedImageItem(ImageItem imageItem) {
      
    }

  /**
     * 更新網路圖片
     */
    public void updateRemoteImageItem(RemoteImageItem remoteImageItem) {
}

  /**
     * 刪除網路圖片
     */
    public void removeRemoteImageItem(RemoteImageItem remoteImageItem) {
}

 

4. 組件代碼

1.ImageEditButton.java 

/**
 * Created by dingzuoqiang on 2017/6/20.
 * Email: [email protected]
 */
public class ImageEditButton extends RelativeLayout {

    private final static String TAG = "ImageEditButton";

    private ImageView imvAddImage;
    private ImageView imvEdit;

    private int imvHeight;
    private int imvWidth;
    public ImageEditButtonListener editButtonListener;

    public ImageEditButton(Context context) {
        this(context, null);
    }


    public ImageEditButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.image_edit_button_view, this, true);
        imvHeight = CommonUtil.dip2px(getContext(), 70);
        imvWidth = imvHeight;
        imvAddImage = (ImageView) findViewById(R.id.imv_add_image);
        imvEdit = (ImageView) findViewById(R.id.imv_edit);
        setImvHeightAndWidth(imvHeight, imvWidth);
        imvAddImage.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                doEditImage();
            }
        });
        imvEdit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                doEditImage2();
            }
        });
    }

    public void setImvHeightAndWidth(int height, int width) {
        this.imvHeight = height;
        this.imvWidth = width;
        ViewGroup.LayoutParams layoutParams = imvAddImage.getLayoutParams();
        layoutParams.width = imvHeight;
        layoutParams.height = imvWidth;
        imvAddImage.setLayoutParams(layoutParams);
    }

    public int getImvHeight() {
        return imvHeight;
    }

    public int getImvWidth() {
        return imvWidth;
    }

    public void setPadding2(int left, int top, int right, int bottom) {
        this.setPadding(left, top, right, bottom);
    }

    public void setBtnImageResource(int resid) {
        imvAddImage.setImageResource(resid);
//        ImageLoaderUtils.loadImageFromDrawable(resid, imvAddImage, null);
    }

    public void reset() {
        imvEdit.setVisibility(GONE);
    }

    public void setEditButtonListener(ImageEditButtonListener editButtonListener) {
        this.editButtonListener = editButtonListener;
    }

    public BaseImageItem getImageItem() {
        Object object = this.getTag();
        if (object instanceof BaseImageItem) return (BaseImageItem) object;
        return null;
    }

    public void displayUI() {
        //
        Object object = this.getTag();
        if (object == null) return;
        if (object instanceof ImageItem) {
            ImageItem imageItem = (ImageItem) object;

            if (TextUtils.isEmpty(imageItem.storedPath))
                return;
            File file = new File(imageItem.storedPath);
            if (file.exists()) {
//                其實Glide載入本地圖片和載入網路圖片調用的方法是一樣的,唯一的區別是說載入SD卡的圖片需要SD卡的許可權,載入網路需要網路許可權
                Glide.with(getContext()).load(file).crossFade().into(imvAddImage);
            }
        } else if (object instanceof RemoteImageItem) {
            // 如果是 remoteImageItem 則需要從讀取圖片,同時不可以裁剪
            RemoteImageItem remoteImageItem = (RemoteImageItem) object;
            Glide.with(getContext()).load(remoteImageItem.thumbUrl).centerCrop().crossFade().into(imvAddImage);
        }

        // TODO
        BaseImageItem baseImageItem = (BaseImageItem) object;
        displayNoteIcons(baseImageItem);
    }

    private void displayNoteIcons(BaseImageItem baseImageItem) {
        imvEdit.setVisibility(VISIBLE);
    }

    private void doEditImage() {
        if (editButtonListener == null) return;

        Object object = this.getTag();
        if (object == null) {
            // add image
            editButtonListener.doAddImage();
        } else {
            //
            if (object instanceof ImageItem) {
                editButtonListener.doEditLocalImage((ImageItem) object);
            } else if (object instanceof RemoteImageItem) {
                editButtonListener.doEditRemoteImage((RemoteImageItem) object);
            }
        }


    }

    private void doEditImage2() {
        if (editButtonListener == null) return;

        Object object = this.getTag();
        if (object != null) {
            //
            if (object instanceof ImageItem) {
                ImageItem imageItem = (ImageItem) object;
                imageItem.isDeleted = true;
                editButtonListener.doEditLocalImage(imageItem);
            } else if (object instanceof RemoteImageItem) {
                RemoteImageItem remoteImageItem = (RemoteImageItem) object;
                remoteImageItem.isDeleted = true;
                editButtonListener.doEditRemoteImage(remoteImageItem);
            }
        }


    }


    public interface ImageEditButtonListener {

        public void doAddImage();

        public void doEditLocalImage(ImageItem imageItem1);

        public void doEditRemoteImage(RemoteImageItem remoteImageItem);
    }


}

 

2.ImageEditContainer.java 

/**
 * Created by dingzuoqiang on 2017/6/20.
 * Email: [email protected]
 */
public class ImageEditContainer extends HorizontalScrollView implements ImageEditButton.ImageEditButtonListener {

    private final static String TAG = "ImageEditContainer";
    public ImageEditContainerListener mEditListener;
    private int idValue = 0;
    ImageEditButton imbAddImage;
    ViewGroup buttonsContainer;

    private int totalImageQuantity = 3;// 總添加數量
    private int mBtnBgResid = 0;

    public ImageEditContainer(Context context) {
        //super(context);
        this(context, null);
    }

    public ImageEditContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        //
        LayoutInflater.from(context).inflate(R.layout.image_edit_container, this, true);

        imbAddImage = (ImageEditButton) findViewById(R.id.imb_add_image);
        imbAddImage.setEditButtonListener(this);
        //
        buttonsContainer = (ViewGroup) findViewById(R.id.lay_container);
        setHorizontalScrollBarEnabled(false);
        setHorizontalFadingEdgeEnabled(false);

    }

    public void setImvHeightAndWidth(int height, int width) {
        for (int i = 0; i < buttonsContainer.getChildCount(); i++) {
            ImageEditButton imageEditButton = (ImageEditButton) buttonsContainer.getChildAt(i);
            if (imageEditButton == null) continue;
            imageEditButton.setImvHeightAndWidth(height, width);
        }
    }

    public void setTotalImageQuantity(int totalImageQuantity) {
        if (totalImageQuantity > 0)
            this.totalImageQuantity = totalImageQuantity;
    }

    public void setBtnImageResource(int resid) {
        mBtnBgResid = resid;
        imbAddImage.setBtnImageResource(mBtnBgResid);
    }

    public List<Object> getAllImageItems() {
        List<Object> allItems = new ArrayList<>();
        for (int i = 0; i < buttonsContainer.getChildCount(); i++) {
            ImageEditButton imageEditButton = (ImageEditButton) buttonsContainer.getChildAt(i);
            if (imageEditButton == null) continue;
            if (imageEditButton.getTag() == null) continue;
            allItems.add(imageEditButton.getTag());
        }
        return allItems;
    }

    /**
     * 添加本地圖片
     */
    public void addNewImages(List<String> storePaths) {

        for (int i = 0; i < storePaths.size(); i++) {
            String path = storePaths.get(i);
            ImageItem imageItem = new ImageItem();
            imageItem.storedPath = path;
            imageItem.id = idValue++;
            Log.i(TAG, "index=" + i + "  id=" + imageItem.id);
            imageItem.index = (buttonsContainer.getChildCount() - 1);
            addBaseImageItemToContainer(imageItem);

        }
    }

    /**
     * 添加本地圖片
     */
    public void addNewImageItem(ImageItem imageItem) {
        if (imageItem == null) return;
        imageItem.id = idValue++;
        imageItem.index = (buttonsContainer.getChildCount() - 1);
        addBaseImageItemToContainer(imageItem);
    }

    public void updateEditedImageItem(ImageItem imageItem) {
        ImageEditButton imageEditButton = getImageEditButtonForImageItemById(imageItem);
        if (imageEditButton == null) {
            return;
        }

        Object originObj = imageEditButton.getTag();
        if (!(originObj instanceof ImageItem)) {
            if (originObj instanceof RemoteImageItem) {
                RemoteImageItem remoteItem = (RemoteImageItem) originObj;
                if (remoteItem.index == imageItem.index) {
                    imageEditButton.setTag(imageItem);
                    imageEditButton.displayUI();
                    return;
                }
                reorderForImageItem(imageItem);
            }
            return;
        }

        ImageItem originImageItem = (ImageItem) originObj;
        if (imageItem.isDeleted) {
            removeButtonContainImageItem(imageItem);
            resetImageItemIndex();
            return;
        } else {

            if (originImageItem.index == imageItem.index) {
                imageEditButton.setTag(imageItem);
                imageEditButton.displayUI();
                return;
            }
            reorderForImageItem(imageItem);
        }
    }


    /**
     * 添加網路圖片
     */
    public void addRemoteImageItem(RemoteImageItem remoteImageItem) {
        addBaseImageItemToContainer(remoteImageItem);
    }

    /**
     * 更新網路圖片
     */
    public void updateRemoteImageItem(RemoteImageItem remoteImageItem) {

        ImageEditButton imageEditButton = getImageEditButtonForImageItemById(remoteImageItem);
        if (imageEditButton == null) {
            if (getAllImageItems().size() > 0) {
                List<Object> objectList = getAllImageItems();
                for (int i = 0; i < objectList.size(); i++) {
                    BaseImageItem baseImageItem = (BaseImageItem) objectList.get(i);
                    removeButtonContainImageItem(baseImageItem);
                }
                //
                objectList.add(0, remoteImageItem);

                for (int i = 0; i < objectList.size(); i++) {
                    addRemoteImageItem((RemoteImageItem) objectList.get(i));
                }
                //
            } else {
                addRemoteImageItem(remoteImageItem);
            }

            return;
        }
        BaseImageItem baseImageItem = (BaseImageItem) imageEditButton.getTag();
        if (baseImageItem instanceof ImageItem) return;
        RemoteImageItem originRemoteItem = (RemoteImageItem) baseImageItem;

        if (remoteImageItem.index == originRemoteItem.index) {
            // index 相同 只是update
            imageEditButton.setTag(remoteImageItem);
            imageEditButton.displayUI();
            return;
        }
        reorderForImageItem(remoteImageItem);
    }

    /**
     * 刪除網路圖片
     */
    public void removeRemoteImageItem(RemoteImageItem remoteImageItem) {

        ImageEditButton imageEditButton = getImageEditButtonForImageItemById(remoteImageItem);
        if (null != imageEditButton && null != imageEditButton.getTag()) {
            BaseImageItem baseImageItem = (BaseImageItem) imageEditButton.getTag();
            if (baseImageItem instanceof ImageItem) return;
            RemoteImageItem originRemoteItem = (RemoteImageItem) baseImageItem;
            removeButtonContainImageItem(remoteImageItem);
            resetImageItemIndex();
        }
    }


    private void reorderForImageItem(BaseImageItem imageItem) {
        removeButtonContainImageItem(imageItem);
        List<BaseImageItem> imageItems = new ArrayList<>();
        imageItems.add(imageItem);
        int count = buttonsContainer.getChildCount();
        for (int i = imageItem.index; i < count; i++) {
            ImageEditButton button = (ImageEditButton) buttonsContainer.getChildAt(i);
            if (button == null) continue;
            BaseImageItem imageItem1 = (BaseImageItem) button.getTag();
            if (imageItem1 == null) continue;
            imageItems.add(imageItem1);
        }
        for (int i = 0; i < imageItems.size(); i++) {
            BaseImageItem item = imageItems.get(i);
            removeButtonContainImageItem(item);
        }
        //
        for (int i = 0; i < imageItems.size(); i++) {
            addBaseImageItemToContainer(imageItems.get(i));
        }

    }

    private void resetImageItemIndex() {
        for (int i = 0; i < buttonsContainer.getChildCount(); i++) {

            try {
                ImageEditButton button = (ImageEditButton) buttonsContainer.getChildAt(i);
                if (button == null) continue;
                BaseImageItem imageItem = (BaseImageItem) button.getTag();
                if (imageItem == null) continue;
                imageItem.index = i;

            } catch (Exception ignored) {

            }
        }
    }


    private ImageEditButton getImageEditButtonForImageItemById(BaseImageItem imageItem) {
        for (int i = 0; i < buttonsContainer.getChildCount(); i++) {
            ImageEditButton imageEditButton = (ImageEditButton) buttonsContainer.getChildAt(i);
            if (imageEditButton == null) continue;
            if (imageEditButton.getImageItem() == null) continue;
            BaseImageItem searchedImageItem = imageEditButton.getImageItem();
            if (imageItem.id.longValue() == searchedImageItem.id.longValue()) {
                return imageEditButton;
            }
        }
        return null;
    }


    /*
    刪除一個 ImageItem
     */
    private void removeButtonContainImageItem(BaseImageItem imageItem) {

        ImageEditButton imageEditButton = getImageEditButtonForImageItemById(imageItem);
        if (imageEditButton == null) return;
        buttonsContainer.removeView(imageEditButton);
        resetImageItemIndex();
        imbAddImage.setVisibility(buttonsContainer.getChildCount() <= totalImageQuantity ? VISIBLE : GONE);
    }


    private void addBaseImageItemToContainer(BaseImageItem imageItem) {
        buttonsContainer.removeView(imbAddImage);

        ImageEditButton imageEditButton = new ImageEditButton(getContext());
        if (mBtnBgResid != 0)
            imageEditButton.setBtnImageResource(mBtnBgResid);
        imageEditButton.setTag(imageItem);
        imageEditButton.setEditButtonListener(this);
//        buttonsContainer.addView(imageEditButton, buttonsContainer.getChildCount(), new RelativeLayout.LayoutParams(nSize, imbAddImage.getHeight()));
        buttonsContainer.addView(imageEditButton, buttonsContainer.getChildCount());
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageEditButton.getLayoutParams();
        layoutParams.rightMargin = CommonUtil.dip2px(getContext(), 5);
        imageEditButton.setLayoutParams(layoutParams);
        imageEditButton.setImvHeightAndWidth(imbAddImage.getImvHeight(), imbAddImage.getImvWidth());
        imageEditButton.displayUI();
        //
        buttonsContainer.addView(imbAddImage, buttonsContainer.getChildCount());
        //
        imbAddImage.setVisibility(buttonsContainer.getChildCount() <= totalImageQuantity ? VISIBLE : GONE);

        resetImageItemIndex();

    }

    /*
    ImageEditButton listener
     */

    public void doAddImage() {
        if (mEditListener != null) {
            mEditListener.doAddImage();
        }
    }

    public void doEditLocalImage(ImageItem imageItem) {
        if (mEditListener != null) {
            mEditListener.doEditLocalImage(imageItem);
        }

    }

    public void doEditRemoteImage(RemoteImageItem remoteImageItem) {
        if (mEditListener != null) {
            mEditListener.doEditRemoteImage(remoteImageItem);
        }

    }
    // -----


    public void setEditListener(ImageEditContainerListener editListener) {
        this.mEditListener = editListener;
    }

    //

    public interface ImageEditContainerListener {
        public void doAddImage();

        public void doEditLocalImage(ImageItem imageItem1);

        public void doEditRemoteImage(RemoteImageItem remoteImageItem);
    }

 

項目源碼下載:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在React和Vue推進下,現在很多人都在使用webpack作為自動化構建工具,但其實在很多時候我們並不是一定需要用到它,gulp這樣的輕量級構建工具就足夠了。 最近一段時間不是太忙,所以就寫了三份配置,用在不同的情況下。 這篇文章介紹第一份配置,也是最簡單的一份。 這份配置我把它稱作demo測試配 ...
  • 1.ajaxfileupload 上傳時會出現如下問題: 2. 網上有很多的解決辦法,在這裡,我又發現了一種,可能你的錯誤會是這個原因引起的 原因是 : 你在一般處理程式中沒有返回前臺需要的數據格式字元串 3.下麵給出一個例子: 1 前臺: 2 <style type="text/css"> 3 . ...
  • 1.將時間戳轉換成時間 var formatDate = function(d) { var now = new Date(d); var year = now.getFullYear(); var month = now.getMonth() + 1; var date = now.getDate ...
  • 做互聯網產品的小伙伴一定不會對“原型”這個詞感到陌生。它就像“用戶體驗”一樣經常被各類人掛在嘴邊。原型是一種讓用戶提前體驗產品、交流設計構想、展示覆雜系統的方式。就本質而言,原型是一種溝通工具。在這裡為大家介紹最常用的6種原型圖文件格式以及各自的優缺點。 一、Mockplus的原型圖格式(.mp) ...
  • 閑言碎語不多說,直接上代碼,轉載請備註來源地,代碼自己看自己悟。 圖片在瀏覽器視窗水平居中展示(圖片尺寸不限制) ...
  • 1、之前我們學習的JS盒子模型中:client系列/offset系列/scrollWidth/scrollHeight都是“只讀”的屬性-> 只能通過屬性獲取值,不能通過屬性修改元素的樣式 2、scrollTop/scrollLeft:滾動條捲去的高度/寬度(這兩個屬性是唯一“可讀寫”的屬性) bo ...
  • 終於考試完了,今天突然想起來前陣子找實習的時候,今日頭條面試官問我,js執行會阻塞DOM樹的解析和渲染,那麼css載入會阻塞DOM樹的解析和渲染嗎?所以,接下來我就來對css載入對DOM樹的解析和渲染做一個測試。 為了完成本次測試,先來科普一下,如何利用chrome來設置下載速度 1. 打開chro ...
  • content-box:padding和border不被包含在定義的width和height之內。對象的實際寬度等於設置的width值和border、padding之和,即 ( Element width = width + border + padding )此屬性表現為標準模式下的盒模型。bor ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...