Android實現(xiàn)樹形文件夾選擇器

主要功能:顯示系統(tǒng)存儲,可設(shè)置自定義節(jié)點圖標(biāo)和文件夾圖標(biāo)以及大小岔冀,可多選个榕,保留view重用機制篡石,不會疊加內(nèi)存,滑動不會卡頓西采。

示例代碼:

mFolderTreeView = (FolderTreeView) findViewById(R.id.mFolderTreeView);
        findViewById(R.id.bn_ok).setOnClickListener(this);

        mFolderTreeView.setShowHidden(false); //不顯示隱藏目錄
        mFolderTreeView.setMultiSelect(true); //多選
        //mFolderTreeView.setLineSpace(16); //行間距
        //mFolderTreeView.setSelectedBkDrawable(R.drawable.item_selected); //選擇項背景
        mFolderTreeView.setShowRootDir(true) //顯示根目錄
                .setShowPublicDir(true) //顯示公共目錄
                .setMultiSelect(true) //多選
                .setNodeIndent(10) //縮進
                .setShowHidden(true) //顯示隱藏目錄
                .setSortType(FolderTreeView.SORT_BY_NAME); //排序

        mFolderTreeView.setOnNodeClickListener(this); //目錄點擊監(jiān)聽
        mFolderTreeView.setOnNodeCheckedListener(this); //目錄選擇監(jiān)聽

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE);
        } else {
            mFolderTreeView.init(); //最后初始化
        }
動畫.gif

Screenshot_20210825-151357.jpg
/**
 * Created by yuanfang235 on 2021/8/24.
 * 文件夾樹形View
 */
 
public class FolderTreeView extends FrameLayout {
 
    private static final String TAG = "fly";
 
    public static final int SORT_AUTO = 0;
    public static final int SORT_BY_NAME = 1;
    public static final int SORT_BY_TIME = 2;
 
    private RecyclerView mRecyclerView;
    private TreeAdapter mTreeAdapter;
    private List<Node> mNodes = new LinkedList<>();
    private LinearLayoutManager mLayoutManager;
    private OnNodeClickListener onNodeClickListener;
    private OnNodeCheckedListener onNodeCheckedListener;
    private float checkIconSize = 16;
    private float nodeIconSize = 12;
    private float folderIconSize = 24;
    private float nodeIndent = -1;
    private float lineSpace = 4;
    private float nodeTextSize = 14;
    private Drawable folderIcon;
    private Drawable nodeOpenIcon;
    private Drawable nodeCloseIcon;
    private Drawable nodeCheckedIcon;
    private Drawable nodeUncheckedIcon;
    private int sortType = SORT_BY_NAME;
    private boolean isSortDescending;
    private boolean isShowHidden = true;
    private boolean isShowRootDir;
    private boolean isShowPublicDir = true;
    private boolean isMultiSelect;
    private List<Node> mSelectedNodes = new LinkedList<>();
    private boolean isSelectedMode;
    private int selectedBkDrawable = 0;
 
    public FolderTreeView(Context context) {
        super(context);
        initView();
    }
 
    public FolderTreeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
 
    public FolderTreeView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }
 
    private void initView() {
        setFocusable(true);
        setFocusableInTouchMode(true);
        nodeOpenIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_node_open);
        nodeCloseIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_node_close);
        nodeCheckedIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_checked);
        nodeUncheckedIcon = ContextCompat.getDrawable(getContext(), R.drawable.ic_unchecked);
        MyScrollView mScrollView = new MyScrollView(getContext());
        mScrollView.setFillViewport(true);
        addView(mScrollView);
        mRecyclerView = new MyRecyclerView(getContext());
        mRecyclerView.setVerticalScrollBarEnabled(true);
        mScrollView.addView(mRecyclerView);
        mLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mTreeAdapter = new TreeAdapter();
        mRecyclerView.setAdapter(mTreeAdapter);
    }
 
    /**
     * 初始化或重置文件夾凰萨,獲取存儲權(quán)限后調(diào)用該方法
     *
     * @return
     */
    public boolean init() {
        if (!canReadStorage()) return false;
        mNodes.clear();
 
        if (isShowRootDir) {
            mNodes.add(makeNode(null, "根目錄", new File("/")));
        }
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            mNodes.add(makeNode(null, "內(nèi)部存儲", Environment.getExternalStorageDirectory()));
        }
        List<File> dirList = getExternalSDCard(getContext().getApplicationContext());
        if (!dirList.isEmpty()) {
            File innerDir = Environment.getExternalStorageDirectory();
            for (int i = 0; i < dirList.size(); i++) {
                if (innerDir != null && dirList.get(i).getAbsolutePath().equals(innerDir.getAbsolutePath())) {
                    continue;
                }
                mNodes.add(makeNode(null, "擴展存儲" + (i + 1), dirList.get(i)));
            }
        }
        if (isShowPublicDir && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            mNodes.add(makeNode(null, "下載", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)));
            mNodes.add(makeNode(null, "圖片", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)));
            mNodes.add(makeNode(null, "相機", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)));
            mNodes.add(makeNode(null, "視頻", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)));
            mNodes.add(makeNode(null, "音樂", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)));
            mNodes.add(makeNode(null, "文檔", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)));
        }
        refresh();
        return true;
    }
 
    /**
     * 生成節(jié)點
     *
     * @param parent
     * @param dir
     * @return
     */
    private Node makeNode(Node parent, String name, File dir) {
        Node node = new Node(name, dir.getAbsolutePath(), dir.lastModified());
        if (parent != null) {
            node.level = parent.level + 1;
        }
        File[] files = dir.listFiles(mDirFilter);
        if (files != null && files.length > 0) {
            node.isShowNodeIcon = true;
        }
        return node;
    }
 
    /**
     * 目錄過濾器
     */
    private FileFilter mDirFilter = new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            return pathname.isDirectory() && (isShowHidden || !pathname.isHidden());
        }
    };
 
    private class TreeAdapter extends RecyclerView.Adapter<TreeAdapter.MyViewHolder> {
 
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new MyViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.tree_item_node, parent, false));
        }
 
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
            final Node node = mNodes.get(position);
            holder.tv_name.setText(node.name);
            holder.tv_name.setTextSize(nodeTextSize);
            if (folderIcon != null) {
                holder.iv_icon.setImageDrawable(folderIcon);
            }
            if (isSelectedMode) {
                holder.iv_node.setVisibility(GONE);
                if (isMultiSelect || mSelectedNodes.contains(node)) {
                    holder.iv_check.setVisibility(VISIBLE);
                } else {
                    holder.iv_check.setVisibility(INVISIBLE);
                }
                if (mSelectedNodes.contains(node)) {
                    holder.iv_check.setImageDrawable(nodeCheckedIcon);
                    holder.vg_item.setBackgroundResource(selectedBkDrawable);
                } else {
                    holder.iv_check.setImageDrawable(nodeUncheckedIcon);
                    holder.vg_item.setBackgroundColor(0);
                }
            } else {
                holder.iv_node.setVisibility(node.isShowNodeIcon ? VISIBLE : INVISIBLE);
                holder.iv_check.setVisibility(GONE);
                holder.iv_check.setImageDrawable(null);
                holder.vg_item.setBackgroundColor(0);
            }
            holder.iv_node.setImageDrawable(node.isExpanded ? nodeOpenIcon : nodeCloseIcon);
            int padding = nodeIndent < 0 ? dip2px(folderIconSize) : dip2px(nodeIndent);
            holder.itemView.setPadding(padding * node.level + dip2px(10), dip2px(lineSpace) / 2, dip2px(10), dip2px(lineSpace) / 2);
            LinearLayout.LayoutParams fiParams = (LinearLayout.LayoutParams) holder.iv_icon.getLayoutParams();
            if (fiParams != null) {
                fiParams.width = dip2px(folderIconSize);
                fiParams.height = dip2px(folderIconSize);
                holder.iv_icon.requestLayout();
            }
            LinearLayout.LayoutParams niParams = (LinearLayout.LayoutParams) holder.iv_node.getLayoutParams();
            if (niParams != null) {
                niParams.width = dip2px(nodeIconSize);
                niParams.height = dip2px(nodeIconSize);
                holder.iv_node.requestLayout();
            }
            LinearLayout.LayoutParams ciParams = (LinearLayout.LayoutParams) holder.iv_check.getLayoutParams();
            if (ciParams != null) {
                ciParams.width = dip2px(checkIconSize);
                ciParams.height = dip2px(checkIconSize);
                holder.iv_check.requestLayout();
            }
            holder.itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (isSelectedMode) {
                        updateSelected(node, position, true);
                    } else {
                        if (node.isShowNodeIcon) {
                            node.isExpanded = !node.isExpanded;
                            updateNodeState(holder, node);
                            if (onNodeClickListener != null) {
                                onNodeClickListener.onNodeClick(FolderTreeView.this, position, new File(node.path), node.isExpanded);
                            }
                        }
                    }
                }
            });
            holder.itemView.setOnLongClickListener(new OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    updateSelected(node, position, false);
                    return true;
                }
            });
 
        }
 
        @Override
        public int getItemCount() {
            return mNodes == null ? 0 : mNodes.size();
        }
 
        class MyViewHolder extends RecyclerView.ViewHolder {
 
            ImageView iv_check;
            ImageView iv_node;
            ImageView iv_icon;
            TextView tv_name;
            ViewGroup vg_item;
 
            public MyViewHolder(View itemView) {
                super(itemView);
                iv_check = itemView.findViewById(R.id.iv_check);
                iv_node = itemView.findViewById(R.id.iv_node);
                iv_icon = itemView.findViewById(R.id.iv_icon);
                tv_name = itemView.findViewById(R.id.tv_name);
                vg_item = itemView.findViewById(R.id.vg_item);
            }
        }
    }
 
    private void updateSelected(Node node, int position, boolean isClick) {
        boolean isChecked;
        if (mSelectedNodes.contains(node)) {
            mSelectedNodes.remove(node);
            isChecked = false;
        } else {
            if (!isMultiSelect) {
                mSelectedNodes.clear();
            }
            mSelectedNodes.add(node);
            isChecked = true;
        }
        if (isClick && isMultiSelect) {
            isSelectedMode = true;
        } else {
            isSelectedMode = !mSelectedNodes.isEmpty();
        }
        refresh();
        if (onNodeCheckedListener != null) {
            onNodeCheckedListener.onNodeChecked(this, position, new File(node.path), isChecked);
        }
    }
 
    private void updateNodeState(TreeAdapter.MyViewHolder holder, Node node) {
        if (node.isExpanded) {
            startNodeOpenAnimation(holder.iv_node);
            addNode(node);
        } else {
            startNodeCloseAnimation(holder.iv_node);
            removeNode(node);
        }
    }
 
    /**
     * 添加節(jié)點
     *
     * @param parent
     */
    private void addNode(Node parent) {
        int position = mNodes.indexOf(parent);
        boolean isLast = (position == mNodes.size() - 1);
        File dir = new File(parent.path);
        File[] files = dir.listFiles(mDirFilter);
        if (files != null && files.length > 0) {
            List<Node> nodes = new ArrayList<>();
            for (File file : files) {
                nodes.add(makeNode(parent, file.getName(), file));
            }
            if (sortType != SORT_AUTO) {
                sort(nodes);
            }
            if (position < mNodes.size() - 1) {
                mNodes.addAll(position + 1, nodes);
            } else {
                mNodes.addAll(nodes);
            }
            parent.isShowNodeIcon = true;
            mTreeAdapter.notifyItemRangeInserted(position + 1, nodes.size());
            if (isLast) {
                mLayoutManager.scrollToPositionWithOffset(position, 0);
            }
        } else {
            parent.isShowNodeIcon = false;
            mTreeAdapter.notifyItemChanged(position);
        }
    }
 
    private void sort(List<Node> nodes) {
        final Collator collator = Collator.getInstance();
        Collections.sort(nodes, new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                if (sortType == SORT_BY_NAME) {
                    return collator.compare(o1.name, o2.name);
                } else if (sortType == SORT_BY_TIME) {
                    if (o1.time < o2.time) {
                        return -1;
                    } else if (o1.time > o2.time) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
                return 0;
            }
        });
        if (isSortDescending) {
            Collections.reverse(nodes);
        }
    }
 
    private void startNodeOpenAnimation(final ImageView nodeView) {
        RotateAnimation animation = new RotateAnimation(0, 90, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(300);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
 
            }
 
            @Override
            public void onAnimationEnd(Animation animation) {
                nodeView.clearAnimation();
                nodeView.setImageDrawable(nodeOpenIcon);
            }
 
            @Override
            public void onAnimationRepeat(Animation animation) {
 
            }
        });
        nodeView.startAnimation(animation);
    }
 
    private void startNodeCloseAnimation(final ImageView nodeView) {
        RotateAnimation animation = new RotateAnimation(0, -90, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(300);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
 
            }
 
            @Override
            public void onAnimationEnd(Animation animation) {
                nodeView.clearAnimation();
                nodeView.setImageDrawable(nodeCloseIcon);
            }
 
            @Override
            public void onAnimationRepeat(Animation animation) {
 
            }
        });
        nodeView.startAnimation(animation);
    }
 
    /**
     * 刪除節(jié)點
     *
     * @param parent
     */
    private void removeNode(Node parent) {
        List<Node> nodes = new ArrayList<>();
        int position = mNodes.indexOf(parent);
        for (int i = position + 1; i < mNodes.size(); i++) {
            Node node = mNodes.get(i);
            if (node.level > parent.level) {
                nodes.add(node);
            } else {
                break;
            }
        }
        if (!nodes.isEmpty()) {
            mNodes.removeAll(nodes);
            mTreeAdapter.notifyItemRangeRemoved(position + 1, nodes.size());
        }
    }
 
    private class Node {
        String name;
        String path;
        long time;
        int level;
        boolean isExpanded;
        boolean isShowNodeIcon;
 
        public Node(String name, String path, long time) {
            this.name = name;
            this.path = path;
            this.time = time;
        }
    }
 
    public void refresh() {
        if (mTreeAdapter != null) {
            mTreeAdapter.notifyDataSetChanged();
        }
    }
 
    /**
     * 存儲權(quán)限
     *
     * @return
     */
    private boolean canReadStorage() {
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
                || ContextCompat.checkSelfPermission(getContext().getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
    }
 
    /**
     * 節(jié)點點擊事件
     *
     * @param onNodeClickListener
     * @return
     */
    public FolderTreeView setOnNodeClickListener(OnNodeClickListener onNodeClickListener) {
        this.onNodeClickListener = onNodeClickListener;
        return this;
    }
 
    /**
     * 節(jié)點選擇事件
     *
     * @param onNodeCheckedListener
     * @return
     */
    public FolderTreeView setOnNodeCheckedListener(OnNodeCheckedListener onNodeCheckedListener) {
        this.onNodeCheckedListener = onNodeCheckedListener;
        return this;
    }
 
    public interface OnNodeClickListener {
        void onNodeClick(FolderTreeView view, int position, File dir, boolean isExpanded);
    }
 
    public interface OnNodeCheckedListener {
        void onNodeChecked(FolderTreeView view, int position, File dir, boolean isChecked);
    }
 
    /**
     * 設(shè)置check框大小
     *
     * @param checkIconSize
     * @return
     */
    public FolderTreeView setCheckIconSize(float checkIconSize) {
        this.checkIconSize = checkIconSize;
        refresh();
        return this;
    }
 
    /**
     * 設(shè)置節(jié)點按鈕大小,單位dip
     *
     * @param nodeIconSize
     * @return
     */
    public FolderTreeView setNodeIconSize(float nodeIconSize) {
        this.nodeIconSize = nodeIconSize;
        refresh();
        return this;
    }
 
    /**
     * 設(shè)置文件夾圖標(biāo)大小,單位dip
     *
     * @param folderIconSize
     * @return
     */
    public FolderTreeView setFolderIconSize(float folderIconSize) {
        this.folderIconSize = folderIconSize;
        refresh();
        return this;
    }
 
    /**
     * 設(shè)置縮進胖眷,單位dip
     *
     * @param nodeIndent
     * @return
     */
    public FolderTreeView setNodeIndent(float nodeIndent) {
        this.nodeIndent = nodeIndent;
        refresh();
        return this;
    }
 
    private int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
 
    /**
     * 設(shè)置行間距武通,單位dip
     *
     * @param lineSpace
     * @return
     */
    public FolderTreeView setLineSpace(float lineSpace) {
        this.lineSpace = lineSpace;
        refresh();
        return this;
    }
 
    /**
     * 設(shè)置節(jié)點文字大小,單位sp
     *
     * @param nodeTextSize
     */
    public FolderTreeView setNodeTextSize(float nodeTextSize) {
        this.nodeTextSize = nodeTextSize;
        refresh();
        return this;
    }
 
    /**
     * 文件夾圖標(biāo)
     *
     * @param iconResId
     * @return
     */
    public FolderTreeView setFolderIcon(int iconResId) {
        folderIcon = ContextCompat.getDrawable(getContext(), iconResId);
        refresh();
        return this;
    }
 
    /**
     * 文件夾圖標(biāo)
     *
     * @param icon
     * @return
     */
    public FolderTreeView setFolderIcon(Drawable icon) {
        folderIcon = icon;
        refresh();
        return this;
    }
 
    /**
     * 排序方式, 初始化之前執(zhí)行
     *
     * @param sortType
     */
    public FolderTreeView setSortType(int sortType) {
        this.sortType = sortType;
        return this;
    }
 
    /**
     * 降序排序, 初始化之前執(zhí)行
     *
     * @param sortDescending
     */
    public FolderTreeView setSortDescending(boolean sortDescending) {
        isSortDescending = sortDescending;
        return this;
    }
 
    /**
     * 顯示隱藏目錄, 初始化之前執(zhí)行
     *
     * @param showHidden
     */
    public FolderTreeView setShowHidden(boolean showHidden) {
        isShowHidden = showHidden;
        return this;
    }
 
 
    /**
     * 設(shè)置節(jié)點圖標(biāo)
     *
     * @param nodeOpenIcon
     * @param nodeCloseIcon
     */
    public FolderTreeView setNodeIcon(Drawable nodeOpenIcon, Drawable nodeCloseIcon) {
        this.nodeOpenIcon = nodeOpenIcon;
        this.nodeCloseIcon = nodeCloseIcon;
        refresh();
        return this;
    }
 
    /**
     * 設(shè)置節(jié)點圖標(biāo)
     *
     * @param nodeOpenIcon
     * @param nodeCloseIcon
     */
    public FolderTreeView setNodeIcon(int nodeOpenIcon, int nodeCloseIcon) {
        return setNodeIcon(ContextCompat.getDrawable(getContext(), nodeOpenIcon),
                ContextCompat.getDrawable(getContext(), nodeCloseIcon));
    }
 
    /**
     * 設(shè)置節(jié)點選擇圖標(biāo)
     * @param checked
     * @param unchecked
     * @return
     */
    public FolderTreeView setNodeCheckIcon(Drawable checked, Drawable unchecked) {
        this.nodeCheckedIcon = checked;
        this.nodeUncheckedIcon = unchecked;
        return this;
    }
 
    /**
     * 設(shè)置節(jié)點選擇圖標(biāo)
     * @param checked
     * @param unchecked
     * @return
     */
    public FolderTreeView setNodeCheckIcon(int checked, int unchecked) {
        return setNodeCheckIcon(ContextCompat.getDrawable(getContext(), checked),
                ContextCompat.getDrawable(getContext(), unchecked));
    }
 
    /**
     * 顯示根目錄
     *
     * @param showRootDir
     */
    public FolderTreeView setShowRootDir(boolean showRootDir) {
        isShowRootDir = showRootDir;
        return this;
    }
 
    /**
     * 顯示公共目錄
     *
     * @param showPublicDir
     */
    public FolderTreeView setShowPublicDir(boolean showPublicDir) {
        isShowPublicDir = showPublicDir;
        return this;
    }
 
    /**
     * 設(shè)置多選模式
     *
     * @param multiSelect
     */
    public FolderTreeView setMultiSelect(boolean multiSelect) {
        isMultiSelect = multiSelect;
        return this;
    }
 
    /**
     * 是選擇模式
     *
     * @return
     */
    public boolean isSelectedMode() {
        return isSelectedMode;
    }
 
    /**
     * 獲取選擇的目錄列表
     *
     * @return
     */
    public List<File> getSelectedFolderList() {
        List<File> dirList = new ArrayList<>();
        for (Node mSelectedNode : mSelectedNodes) {
            dirList.add(new File(mSelectedNode.path));
        }
        return dirList;
    }
 
    /**
     * 獲取單選目錄
     *
     * @return
     */
    public File getSelectedFolder() {
        if (isMultiSelect || mSelectedNodes.isEmpty()) {
            return null;
        } else {
            return new File(mSelectedNodes.get(0).path);
        }
    }
 
    /**
     * 選定背景
     *
     * @param selectedBkDrawable
     */
    public FolderTreeView setSelectedBkDrawable(int selectedBkDrawable) {
        this.selectedBkDrawable = selectedBkDrawable;
        return this;
    }
 
    static class MyScrollView extends HorizontalScrollView {
 
        public MyScrollView(Context context) {
            super(context);
        }
 
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
 
        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
 
        private float lastX, lastY;
 
        @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
 
            boolean intercept = super.onInterceptTouchEvent(e);
 
            switch (e.getAction() & e.getActionMasked()) {
 
                case MotionEvent.ACTION_DOWN:
                    lastX = e.getX();
                    lastY = e.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float x = Math.abs(e.getX() - lastX);
                    float y = Math.abs(e.getY() - lastY);
                    if ((x > 0 || y > 0) && y >= x) {
                        requestDisallowInterceptTouchEvent(true);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    intercept = false;
                    break;
            }
            return intercept;
        }
 
    }
 
    private static class MyRecyclerView extends RecyclerView {
 
        public MyRecyclerView(Context context) {
            super(context);
        }
 
        public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
 
        public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
 
    }
 
    private static void print(Object... info) {
        if (info == null || info.length == 0) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (Object o : info) {
            sb.append(o != null ? o.toString() + ", " : ", ");
        }
        String msg = sb.toString();
        if (msg.endsWith(",")) msg = sb.deleteCharAt(sb.length() - 1).toString();
        Log.e(TAG, msg);
    }
 
    private static List<File> getExternalSDCard(Context context) {
        List<File> pathsList = new ArrayList<>();
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            Method method = StorageManager.class.getDeclaredMethod("getVolumePaths");
            method.setAccessible(true);
            Object result = method.invoke(storageManager);
            if (result != null && result instanceof String[]) {
                String[] pathes = (String[]) result;
                if (pathes != null && pathes.length > 0) {
                    StatFs statFs;
                    for (String path : pathes) {
                        File file = new File(path);
                        if (!android.text.TextUtils.isEmpty(path) && file.exists()) {
                            statFs = new StatFs(path);
                            if (statFs.getBlockCount() * statFs.getBlockSize() != 0) {
                                pathsList.add(file);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pathsList;
    }
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
            if (isSelectedMode) {
                cancelSelectedMode();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
 
    private void cancelSelectedMode() {
        mSelectedNodes.clear();
        isSelectedMode = false;
        refresh();
    }
}

庫文件和demo下載地址:https://github.com/yuanfang235/FolderTreeView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末珊搀,一起剝皮案震驚了整個濱河市冶忱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌境析,老刑警劉巖囚枪,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異劳淆,居然都是意外死亡链沼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門沛鸵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來括勺,“玉大人,你說我怎么就攤上這事曲掰〖埠矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵栏妖,是天一觀的道長拾氓。 經(jīng)常有香客問我,道長底哥,這世上最難降的妖魔是什么咙鞍? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮趾徽,結(jié)果婚禮上续滋,老公的妹妹穿的比我還像新娘。我一直安慰自己孵奶,他們只是感情好疲酌,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著了袁,像睡著了一般朗恳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上载绿,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天粥诫,我揣著相機與錄音,去河邊找鬼崭庸。 笑死怀浆,一個胖子當(dāng)著我的面吹牛谊囚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播执赡,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼镰踏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沙合?” 一聲冷哼從身側(cè)響起款侵,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤刀荒,失蹤者是張志新(化名)和其女友劉穎打掘,沒想到半個月后单刁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡猜拾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年即舌,在試婚紗的時候發(fā)現(xiàn)自己被綠了佣盒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挎袜。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肥惭,靈堂內(nèi)的尸體忽然破棺而出盯仪,到底是詐尸還是另有隱情,我是刑警寧澤蜜葱,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布全景,位于F島的核電站,受9級特大地震影響牵囤,放射性物質(zhì)發(fā)生泄漏爸黄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一揭鳞、第九天 我趴在偏房一處隱蔽的房頂上張望炕贵。 院中可真熱鬧,春花似錦野崇、人聲如沸称开。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳖轰。三九已至,卻和暖如春扶镀,著一層夾襖步出監(jiān)牢的瞬間蕴侣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工臭觉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留睛蛛,地道東北人鹦马。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像忆肾,于是被迫代替她去往敵國和親荸频。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容