Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现

简介: Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现 我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用ListView太久远了,导致对他已经有浓厚的感情了,我们之前也是写过...

Android高级控件(六)——自定义ListView高仿一个QQ可拖拽列表的实现


我们做一些好友列表或者商品列表的时候,居多的需求可能就是需要列表拖拽了,而我们选择了ListView,也是因为使用ListView太久远了,导致对他已经有浓厚的感情了,我们之前也是写过几篇关于ListView的博文

而今天我们就来实现以下课拖拽的方案,大家可以借鉴以下

我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的ListView的数据,但是他的Adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后自定义一个listview去操作,所以我们先把准备的工作做好,比如?

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/iv_logo"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentLeft="true"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dp"/>

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/iv_logo"/>

</RelativeLayout>

这就只有一个头像和一句话了,然后我们把实体类也给写完了

DragBean

package com.liuguilin.draglistviewsample.entity;

/*
 *  项目名:  DragListViewSample 
 *  包名:    com.liuguilin.draglistviewsample.entity
 *  文件名:   DragBean
 *  创建者:   LGL
 *  创建时间:  2016/8/29 22:49
 *  描述:    实体类
 */

public class DragBean {

    private int ivId;
    private String text;

    public DragBean() {

    }

    public DragBean(int ivId, String text) {
        this.ivId = ivId;
        this.text = text;
    }

    public int getIvId() {
        return ivId;
    }


    public String getText() {
        return text;
    }

}

ok,其实很简单,id是图片,然后是文本,这样我们就可以来实现一个Adapter了,这里我用的是ArrayAdapter这样能让我们插入和删除很轻松

DragAdapter

package com.liuguilin.draglistviewsample.adapter;

/*
 *  项目名:  DragListViewSample 
 *  包名:    com.liuguilin.draglistviewsample.adapter
 *  文件名:   DragAdapter
 *  创建者:   LGL
 *  创建时间:  2016/8/29 22:41
 *  描述:    拖拽列表的数据源
 */

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.liuguilin.draglistviewsample.R;
import com.liuguilin.draglistviewsample.entity.DragBean;

import java.util.List;

public class DragAdapter extends ArrayAdapter<DragBean> {

    /**
     * 构造方法
     *
     * @param context
     * @param mList
     */
    public DragAdapter(Context context, List<DragBean> mList) {
        super(context, 0, mList);
    }

    /**
     * 实现View
     *
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        ViewHolder viewHolder;
        if (convertView == null) {
            view = View.inflate(getContext(), R.layout.list_item, null);
            viewHolder = new ViewHolder();
            viewHolder.imageView = (ImageView) view
                    .findViewById(R.id.iv_logo);
            viewHolder.textView = (TextView) view.findViewById(R.id.textView);
            view.setTag(viewHolder);
        } else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }

        viewHolder.imageView.setImageResource(getItem(position).getIvId());
        viewHolder.textView.setText(getItem(position).getText());
        return view;
    }

    /**
     * 缓存
     */
    static class ViewHolder {
        ImageView imageView;
        TextView textView;
    }

}

好的,其实到这里,他就是一个最普通的ListView了,我们给他填充点数据

MainActivity

package com.liuguilin.draglistviewsample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.liuguilin.draglistviewsample.adapter.DragAdapter;
import com.liuguilin.draglistviewsample.entity.DragBean;
import com.liuguilin.draglistviewsample.view.DragListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    //列表
    private DragListView mListView;
    //数据
    private List<DragBean> mList = new ArrayList<>();
    //数据源
    private DragAdapter adapter;

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

        initView();
    }

    /**
     * 初始化View
     */
    private void initView() {
        mListView = (DragListView) findViewById(R.id.mListView);

        //新增數據
        for (int i = 0; i < 30; i++) {
            DragBean bean = new DragBean(R.mipmap.ic_launcher, "刘某人程序员" + i);
            mList.add(bean);
        }
        //初始化数据源
        adapter = new DragAdapter(this,mList);
        mListView.setAdapter(adapter);
    }
}

现在可以看看实际的效果了

这里写图片描述

现在我们可以重写我们的ListView了

我们首先拦截他的事件

 /**
     * 获取触点所在条目的位置
     * 获取选中条目的图片
     * 事件的拦截机制
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //识别动作
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //获取触点的坐标
                int x = (int) ev.getX();
                int y = (int) ev.getY();
                //这样就可以计算我按到哪个条目上了
                mStartPosition = mEndPosition = pointToPosition(x, y);
                //判断触点是否在logo的区域
                ViewGroup itemView = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());
                //记录手指在条目中的相对Y坐标
                dragPoint = y - itemView.getTop();
                //ListView在屏幕中的Y坐标
                dragOffset = (int) (ev.getRawY() - y);
                //拖动的图标
                View dragger = itemView.findViewById(R.id.iv_logo);
                //判断触点是否在logo区域
                if (dragger != null && x < dragger.getRight() + 10) {
                    //定义ListView的滚动条目
                    //上
                    upScroll = getHeight() / 3;
                    //下
                    downScroll = getHeight() * 2 / 3;
                    //获取选中的图片/截图
                    itemView.setDrawingCacheEnabled(true);
                    //获取截图
                    Bitmap bitMap = itemView.getDrawingCache();
                    //图片滚动
                    startDrag(bitMap, y);
                }
                break;
        }
        //还会传递事件到子View
        return false;
    }

获取到他的position之后我们直接截图,并且显示我们的window,这里做的事情就比较多了我们还要判断是否点击的是头像才去显示window

 /**
     * 图片在Y轴,也就是上下可滚动
     *
     * @param bitMap
     * @param y
     */
    private void startDrag(Bitmap bitMap, int y) {
        //窗体仿照
        wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        //设置窗体参数
        lParams = new WindowManager.LayoutParams();
        //设置宽高
        lParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        lParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        //属性
        lParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        //设置半透明
        lParams.alpha = 0.8f;
        //设置居中
        lParams.gravity = Gravity.TOP;
        //设置xy
        lParams.x = 0;
        lParams.y = y-dragPoint + dragOffset;
        //属性
        lParams.format = PixelFormat.TRANSLUCENT;
        //设置动画
        lParams.windowAnimations = 0;
        //图片
        dragImageView = new ImageView(getContext());
        //设置截图
        dragImageView.setImageBitmap(bitMap);
        //添加显示窗体
        wm.addView(dragImageView, lParams);
    }

好的,我们初始化一下window,光显示还不行呢,我们还要可以滑动,怎么监听?onTouchEvent的ACTION_MOVE事件是可以做到的


    /**
     * 触摸事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //错误的位置
        if (dragImageView != null && mEndPosition != INVALID_POSITION) {
            //在滑动事件中控制上下滑动
            switch (ev.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    //直接获取到Y坐标进行移动
                    int moveY = (int) ev.getY();
                    doDrag(moveY);
                    break;
                //停止拖动成像
                case MotionEvent.ACTION_UP:
                    int upY = (int) ev.getY();
                    stopDrag();
                    onDrag(upY);
                    break;
            }
        }
        //拦截到事件
        return true;
    }

我们在移动的时候不断的去更新他的位置

    /**
     * 控制窗体移动
     *
     * @param moveY
     */
    private void doDrag(int moveY) {
        if (dragImageView != null) {
            lParams.y = moveY - dragPoint + dragOffset;
            wm.updateViewLayout(dragImageView, lParams);
        }
        //判断移动到分割线 返回-1
        int tempLine = pointToPosition(0, moveY);
        //我们处理他
        if (tempLine != INVALID_POSITION) {
            //只要你不移动到分割线 我才处理
            mEndPosition = tempLine;
        }

        //拖拽时滚动、滚动速度
        int scrollSpeed = 0;
        //上滚
        if (moveY < upScroll) {
            scrollSpeed = 10;
            //下滚
        } else if (moveY > downScroll) {
            scrollSpeed = -10;
        }
        //开始滚动
        if (scrollSpeed != 0) {
            //计算条目的Y坐标
            int dragItemY = getChildAt(mEndPosition - getFirstVisiblePosition()).getTop();
            //当前速度
            int dy = dragItemY + scrollSpeed;
            //设置
            setSelectionFromTop(mEndPosition, dy);
        }
    }

当你移动完成之后,我就可以停止你的window体了

     /**
     * 停止的位置
     */
    private void stopDrag() {
        //直接移除窗体
        if (dragImageView != null) {
            wm.removeView(dragImageView);
            dragImageView = null;
        }

    }

这样,我就直接拼接你的position达到一个拖拽的效果


    /**
     * 最终开始成像
     *
     * @param upY
     */
    private void onDrag(int upY) {
        //分割线的处理
        //判断移动到分割线 返回-1
        int tempLine = pointToPosition(0, upY);
        //我们处理他
        if (tempLine != INVALID_POSITION) {
            //只要你不移动到分割线 我才处理
            mEndPosition = tempLine;
        }

        /**
         * 你在最上方就直接落在第一个最下方就直接最下面一个
         */

        //上边界处理
        if (upY < getChildAt(1).getTop()) {
            mEndPosition = 1;
            //下边界处理
        } else if (upY > getChildAt(getChildCount() - 1).getTop()) {
            mEndPosition = getAdapter().getCount() - 1;
        }

        //开始更新item顺序
        if (mEndPosition > 0 && mEndPosition < getAdapter().getCount()) {
            DragAdapter adapter = (DragAdapter) getAdapter();
            //删除原来的条目
            adapter.remove(adapter.getItem(mStartPosition));
            //更新
            adapter.insert(adapter.getItem(mStartPosition), mEndPosition);
        }
    }

全部代码贴上

DragListView

package com.liuguilin.draglistviewsample.view;


/*
 *  项目名:  DragListViewSample 
 *  包名:    com.liuguilin.draglistviewsample.view
 *  文件名:   DragListView
 *  创建者:   LGL
 *  创建时间:  2016/8/29 20:50
 *  描述:    自定义高仿QQ列表可拖拽的ListView
 */

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ListView;

import com.liuguilin.draglistviewsample.R;
import com.liuguilin.draglistviewsample.adapter.DragAdapter;

public class DragListView extends ListView {

    //按下选中的position
    private int mStartPosition;
    //需要达到的position
    private int mEndPosition;
    //手指在条目中的相对Y坐标
    private int dragPoint;
    //ListView在屏幕中的Y坐标
    private int dragOffset;
    //上
    private int upScroll;
    //下
    private int downScroll;
    //窗体
    private WindowManager wm;
    //显示的截图
    private ImageView dragImageView;
    //窗体参数
    private WindowManager.LayoutParams lParams;

    //构造方法
    public DragListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 获取触点所在条目的位置
     * 获取选中条目的图片
     * 事件的拦截机制
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //识别动作
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //获取触点的坐标
                int x = (int) ev.getX();
                int y = (int) ev.getY();
                //这样就可以计算我按到哪个条目上了
                mStartPosition = mEndPosition = pointToPosition(x, y);
                //判断触点是否在logo的区域
                ViewGroup itemView = (ViewGroup) getChildAt(mStartPosition - getFirstVisiblePosition());
                //记录手指在条目中的相对Y坐标
                dragPoint = y - itemView.getTop();
                //ListView在屏幕中的Y坐标
                dragOffset = (int) (ev.getRawY() - y);
                //拖动的图标
                View dragger = itemView.findViewById(R.id.iv_logo);
                //判断触点是否在logo区域
                if (dragger != null && x < dragger.getRight() + 10) {
                    //定义ListView的滚动条目
                    //上
                    upScroll = getHeight() / 3;
                    //下
                    downScroll = getHeight() * 2 / 3;
                    //获取选中的图片/截图
                    itemView.setDrawingCacheEnabled(true);
                    //获取截图
                    Bitmap bitMap = itemView.getDrawingCache();
                    //图片滚动
                    startDrag(bitMap, y);
                }
                break;
        }
        //还会传递事件到子View
        return false;
    }

    /**
     * 图片在Y轴,也就是上下可滚动
     *
     * @param bitMap
     * @param y
     */
    private void startDrag(Bitmap bitMap, int y) {
        //窗体仿照
        wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        //设置窗体参数
        lParams = new WindowManager.LayoutParams();
        //设置宽高
        lParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        lParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        //属性
        lParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        //设置半透明
        lParams.alpha = 0.8f;
        //设置居中
        lParams.gravity = Gravity.TOP;
        //设置xy
        lParams.x = 0;
        lParams.y = y-dragPoint + dragOffset;
        //属性
        lParams.format = PixelFormat.TRANSLUCENT;
        //设置动画
        lParams.windowAnimations = 0;
        //图片
        dragImageView = new ImageView(getContext());
        //设置截图
        dragImageView.setImageBitmap(bitMap);
        //添加显示窗体
        wm.addView(dragImageView, lParams);
    }

    /**
     * 触摸事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //错误的位置
        if (dragImageView != null && mEndPosition != INVALID_POSITION) {
            //在滑动事件中控制上下滑动
            switch (ev.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    //直接获取到Y坐标进行移动
                    int moveY = (int) ev.getY();
                    doDrag(moveY);
                    break;
                //停止拖动成像
                case MotionEvent.ACTION_UP:
                    int upY = (int) ev.getY();
                    stopDrag();
                    onDrag(upY);
                    break;
            }
        }
        //拦截到事件
        return true;
    }

    /**
     * 最终开始成像
     *
     * @param upY
     */
    private void onDrag(int upY) {
        //分割线的处理
        //判断移动到分割线 返回-1
        int tempLine = pointToPosition(0, upY);
        //我们处理他
        if (tempLine != INVALID_POSITION) {
            //只要你不移动到分割线 我才处理
            mEndPosition = tempLine;
        }

        /**
         * 你在最上方就直接落在第一个最下方就直接最下面一个
         */

        //上边界处理
        if (upY < getChildAt(1).getTop()) {
            mEndPosition = 1;
            //下边界处理
        } else if (upY > getChildAt(getChildCount() - 1).getTop()) {
            mEndPosition = getAdapter().getCount() - 1;
        }

        //开始更新item顺序
        if (mEndPosition > 0 && mEndPosition < getAdapter().getCount()) {
            DragAdapter adapter = (DragAdapter) getAdapter();
            //删除原来的条目
            adapter.remove(adapter.getItem(mStartPosition));
            //更新
            adapter.insert(adapter.getItem(mStartPosition), mEndPosition);
        }
    }

    /**
     * 停止的位置
     */
    private void stopDrag() {
        //直接移除窗体
        if (dragImageView != null) {
            wm.removeView(dragImageView);
            dragImageView = null;
        }

    }

    /**
     * 控制窗体移动
     *
     * @param moveY
     */
    private void doDrag(int moveY) {
        if (dragImageView != null) {
            lParams.y = moveY - dragPoint + dragOffset;
            wm.updateViewLayout(dragImageView, lParams);
        }
        //判断移动到分割线 返回-1
        int tempLine = pointToPosition(0, moveY);
        //我们处理他
        if (tempLine != INVALID_POSITION) {
            //只要你不移动到分割线 我才处理
            mEndPosition = tempLine;
        }

        //拖拽时滚动、滚动速度
        int scrollSpeed = 0;
        //上滚
        if (moveY < upScroll) {
            scrollSpeed = 10;
            //下滚
        } else if (moveY > downScroll) {
            scrollSpeed = -10;
        }
        //开始滚动
        if (scrollSpeed != 0) {
            //计算条目的Y坐标
            int dragItemY = getChildAt(mEndPosition - getFirstVisiblePosition()).getTop();
            //当前速度
            int dy = dragItemY + scrollSpeed;
            //设置
            setSelectionFromTop(mEndPosition, dy);
        }
    }
}

然后我们引用

layout_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <com.liuguilin.draglistviewsample.view.DragListView
        android:id="@+id/mListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

对了,别忘记了添加窗体的权限哦

 <!--窗口权限-->
 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

做完这一系列事情之后,就觉得这个设想其实是对的,然后我们可以尝试性的运行一下

这里写图片描述

运行时成功的,说明我们的设想是没有问题的,但是随着而来的,也是诸多的bug,所以呢,这也只是思想的参考,不过这些bug我有时间野就修复一下,应该是可以的,好的,本篇博文就先到这里!

有兴趣的加群:555974449

DragListViewSample:http://download.csdn.net/detail/qq_26787115/9616355

目录
相关文章
|
14天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
37 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
2月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
90 11
|
4月前
|
XML 存储 Java
浅谈Android的TextView控件
浅谈Android的TextView控件
59 0
|
5月前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
76 3
|
6月前
|
前端开发 Android开发 开发者
安卓开发中的自定义视图:构建你的第一个控件
【8月更文挑战第26天】在安卓开发的浩瀚海洋中,自定义视图是一块充满魔力的乐土。它不仅是开发者展示创造力的舞台,更是实现独特用户体验的关键。本文将带你步入自定义视图的世界,从基础概念到实战应用,一步步教你如何打造自己的第一个控件。无论你是初学者还是有经验的开发者,这篇文章都将为你的开发之旅增添新的风景。
|
6月前
|
Android开发 开发者 UED
Android项目架构设计问题之加载数据到列表如何解决
Android项目架构设计问题之加载数据到列表如何解决
51 0
|
7月前
|
XML 数据格式
Android-自定义三角形评分控件
Android-自定义三角形评分控件
63 0
|
9月前
|
XML Java Android开发
如何美化android程序:自定义ListView背景
如何美化android程序:自定义ListView背景
87 2
|
Android开发
Android自定义ListView和GridView解决滑动冲突和显示不全
Android自定义ListView和GridView解决滑动冲突和显示不全
182 0
|
Android开发
Android项目实战(十五):自定义不可滑动的ListView和GridView
原文:Android项目实战(十五):自定义不可滑动的ListView和GridView 不可滑动的ListView (RecyclweView类似) public class NoScrollListView extends ListView { public NoScrollLi...
980 0

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 9
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 10
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 1
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    24
  • 2
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    33
  • 3
    Android历史版本与APK文件结构
    121
  • 4
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 5
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    57
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 8
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 9
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    119
  • 10
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29