`
xuela_net
  • 浏览: 494354 次
文章分类
社区版块
存档分类
最新评论

Android仿人人客户端(v5.7.1)——采用ViewGroup做父容器,实现左侧滑动菜单(三)

 
阅读更多

转载请标明出处:http://blog.csdn.net/android_ls/article/details/8761410

前面已实现以滑动的方式显示或隐藏左侧菜单,采用的父容器是自定义类继承自RelativeLayout来实现的。看到有网友留言说,采用父容器自定义类继承自ViewGroup去实现,这篇我就讲解下采用继承自ViewGroup的方式去如何实现。

自定义类让其继承自ViewGroup类后,编译器强制添加一个带Context类型参数的构造方法和onLayout()方法,代码如下:

package com.everyone.android.widget;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

/**
 * 功能描述:手指在屏幕上左右滑动时,该类的实例负责让其子View根据用户的手势左右偏移(滚动)
 * 父容器采用ViewGroup
 * @author android_ls
 */
public class HorizontalScrollerContainer extends ViewGroup {

    public HorizontalScrollerContainer(Context context) {
        super(context);
    }
   
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {        }

}

通过阅读上面的代码片段,引发的思考,ViewGroup类有无参构造方法吗?答案是没有;至于onLayout()方法,肯定是一个抽象方法了,那么ViewGroup类肯定是抽象类。在Java的继承体系中,父类(ViewGroup)中有抽象方法,子类继承父类必须(强制性的)去实现父类的抽象方法或者把自己也定义成抽象的。既然onLayout()方法是抽象方法,我们自定义的类去继承了ViewGroup类,最后自定义的类是要用来诞生对象的,那肯定不能也定义成抽象的。我们就去实现它,代码如下:

  @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
        }
    }

既然容器有了,我们就向其中添加子View,在Activity的onCreate()方法添加如下代码:

       // 父容器
        HorizontalScrollerContainer mSlideContainer = new HorizontalScrollerContainer(mContext);
        
        // 左侧面板
        LeftPanelLayout mLeftPanelLayout = new LeftPanelLayout(mContext);
        LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        // 添加子View
        mSlideContainer.addView(mLeftPanelLayout, layoutParams);
        
        // 设置要显示的视图
        setContentView(mSlideContainer);

我们运行程序,效果图如下:



看到这张效果图,有人可能要说,我们自定义的父容器HorizontalScrollerContainer类的对象,到底有没有设置到Window(哪来的Window对象呢?查看过Android源码的朋友肯定知道,没翻过源码的见这行:getWindow().setContentView(view),这不是重点。)上,我们为父容器添加一张背景图,代码如下:

  public HorizontalScrollerContainer(Context context) {
        super(context);
        this.setBackgroundResource(R.drawable.v5_3_0_guide_pic1);
    }

运行效果图:


通过上面的验证,说明我们的父容器View是已添加到PhoneWindow上了,只是其中的子View没有显示出来。如何才能让父容器中的子View显示出来呢? 我们还得去重写父容器ViewGroup的onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,代码如下:

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

运行看效果:


这次明显达到了我们的预期(目的),效果是实现了,到底为什么之前没达到预期效果呢,应用框架层明明强制我(自定义类继承自ViewGroup类)去实现的只有onLayout()方法,我按你(应用框架)的要求去做了,为什么达不到预期效果呢?

当你为一个Activty 的PhoneWindow设置一个可见的View(ViewGroup),并且运行这个Activty时,应用框架层的调用(控制反转)ViewGroup的显示视图相关方法的顺序:onAttachedToWindow-->onMeasure-->onSizeChanged-->onLayout(注:其它相关的方法我们不关注,这里就没列出来)。下面来解释下这几个我们关注的方法的含义:

1、onAttachedToWindow:当View附加到一个窗体上时,应用框架层调用此方法。

2、onMeasure:View自己调用此方法,测量自己及其子View的实际宽度和高度。

3、onSizeChanged当View大小改变时,调用此方法

4、onLayout:View自己调用此方法,为所有子View分配显示空间的大小,可能会与子View定义时设置的大小不一样。比如:父容器是LinearLayout,里面的子View排列方向是竖直方式,我向父容器中添加第一个子View(A),设置宽度为fill_parent,高度为50dip;我再向父容器中添加第二个子View(B),设置宽度为fill_parent,高度也为fill_parent。在这种情况下,子View(B)的高度为多少? 是fill_parent吗?肯定不是啦,是父容器的高度减去子View(A)的高度。(可能会与子View定义时设置的大小不一样)

我的个人观点:既然编写自定义类去继承ViewGroup后,子类必须去实现onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,为什么不把onMeasure()方法在ViewGroup类中定义成抽象的呢?翻看源码,会发现:

onMeasure()是定义在View类中的,源码如下:

   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

ViewGroup类完全可以重写父类的onMeasure()方法,将其修改成抽象方法。那么当我们想自定义类去继承ViwGroup类,都会去实现onMeasure()方法,就不会出现按应用框架要求的去做,而没达到预期的,我们想要的结果。

编写两个类,进行测试,TestA类源码:

package com.everyone.android.widget;

public class TestA { 
    
 public void test(){
     System.out.println("Hello World");
 } 
 
}

TestB类源码:

package com.everyone.android.widget;

public abstract class TestB extends TestA{
    public abstract void test();
}

通过上面的测试,大家看到这么写,子类是可以修改父类的方法修饰词的。

假如:Android应用框架里,ViewGroup类里重写了父类的onMeasure()方法,在ViewGroup类中添加如下代码:

 protected abstract void onMeasure(int widthMeasureSpec, int heightMeasureSpec);

我们自已的类继承ViewGroup后,我们实现onMeasure()方法的代码如下:

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
        
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

至于Android应用框架层为什么没按我们预想的那样去实现,自然有他们的道理,呵呵。我个人觉的若在子类中必须重写父类的某个方法后,才既能满足框架层又能让使用框架的达到预期的效果。干嘛不把该方法定义成抽象的呢?何乐而不为。
好了,到这里这一篇的重点聊完了。其它的关于用户触摸屏幕事件的分发、拦截和处理(响应)与前面的采用RelativeLayout做父容器时是一样的。

采用ViewGroup做父容器,实现水平滑动显示或隐藏左侧菜单的完整代码如下:

package com.everyone.android.widget;

import android.content.Context;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * 功能描述:手指在屏幕上左右滑动时,该类的实例负责让其子View根据用户的手势左右偏移(滚动)
 * 父容器采用ViewGroup
 * @author android_ls
 */
public class HorizontalScrollerContainer extends ViewGroup {

    private static final String TAG = "ScrollerContainer";

    private Scroller mScroller;

    private VelocityTracker mVelocityTracker;

    /**
     * 手柄(手把)的宽度
     */
    private int mHandlebarWidth;

    /**
     * 在偏移过程中,动画持续的时间
     */
    private static final int ANIMATION_DURATION_TIME = 300;
    
    /**
     * 记录当前的滑动结束后的状态,左侧面板是否可见
     * true  向右滑动(左侧面板处于可见)
     * false 向左滑动(左侧面板处于不可见)
     */
    private boolean mPanelInvisible;
    
    /**
     * 是否已滑动结束
     */
    private boolean mFinished;
    
    /**
     * 是否允许滚动
     * 满足的条件:
     *     左侧面板可见,当前手指按下的坐标x值 ,是在手柄宽度范围内;
     *     左侧面板不可见,当前手指按下的坐标x值 < 手柄宽度
     */
    private boolean mAllowScroll;
    
    /**
     * 是否满足响应单击事件的条件
     * 满足的条件:左侧面板可见,当前手指按下的坐标x值 ,是在手柄宽度范围内
     */
    private boolean isClick;
    
    public HorizontalScrollerContainer(Context context) {
        super(context);
        // this.setBackgroundResource(R.drawable.v5_3_0_guide_pic1);
        
        mScroller = new Scroller(context);
        mHandlebarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());
    }
    
    // 测量自己及其子View的实际宽度和高度
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    
    /*
     * 为所有子View分配显示空间的大小 ,可能会与子View定义时设置的大小不一样。
     * 比如:父容器是LinearLayout,里面的子View排列方向是竖直方式,我向父容器中添加第一个子View(A),
     * 设置宽度为fill_parent,高度为50dip;我再向父容器中添加第二个子View(B),设置宽度为fill_parent,高度也为fill_parent。
     * 在这种情况下,子View(B)的高度为多少? 是fill_parent吗?肯定不是啦,是父容器的高度减去子View(A)的高度。
     * (可能会与子View定义时设置的大小不一样)
     * @see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
        }
    }
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG, "dispatchTouchEvent()");
        
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i(TAG, "dispatchTouchEvent():  ACTION_DOWN");
            
            mFinished = mScroller.isFinished();
            if(mFinished){
                int x = (int) ev.getX();
                int width = getWidth();
                
                if(mPanelInvisible)// 左侧面板可见
                {
                    if(x > (width - mHandlebarWidth)){ // 当前手指按下的坐标x值 ,是在手柄宽度范围内
                        isClick = true;
                        mAllowScroll = true;
                        return true;
                    } else {
                        isClick = false;
                        mAllowScroll = false;
                    }
                } else { // 左侧面板不可见
                    if(x < mHandlebarWidth ){ // 当前手指按下的坐标x值 < 手柄宽度 (也就是说在手柄宽度范围内,是可以相应用户的向右滑动手势)
                        mAllowScroll = true;
                    }else{
                        mAllowScroll = false;
                    }
                }
                
            } else {
                // 当前正在滚动子View,其它的事不响应
                return false;
            }
            
            break;

        case MotionEvent.ACTION_MOVE:
            Log.i(TAG, "dispatchTouchEvent():  ACTION_MOVE");
            int margin = getWidth() - (int) ev.getX();
            if (margin < mHandlebarWidth && mAllowScroll) {
                
                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE margin = " + margin + "\t mHandlebarWidth = " + mHandlebarWidth);
                return true;
            }
            
            break;
        case MotionEvent.ACTION_UP:
            Log.i(TAG, "dispatchTouchEvent():  ACTION_UP");
            
            if (isClick && mPanelInvisible && mAllowScroll) {
                isClick = false;
                mPanelInvisible = false;
                
                int scrollX = getChildAt(1).getScrollX();
                mScroller.startScroll(scrollX, 0, -scrollX, 0, ANIMATION_DURATION_TIME);
                invalidate();
                
                return true;
            }
            
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.i(TAG, "dispatchTouchEvent():  ACTION_CANCEL");
            break;
        default:
            break;
        }
        
        return super.dispatchTouchEvent(ev);
    }
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e(TAG, "onInterceptTouchEvent()");
        
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i(TAG, "onInterceptTouchEvent():  ACTION_DOWN");
            mFinished = mScroller.isFinished();
            if(!mFinished){
                return false;
            }
            
            break;
        case MotionEvent.ACTION_MOVE:
            Log.i(TAG, "onInterceptTouchEvent():  ACTION_MOVE");
            
            mVelocityTracker = VelocityTracker.obtain();
            mVelocityTracker.addMovement(ev);
            
            // 一秒时间内移动了多少个像素
            mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.getMaximumFlingVelocity());
            float velocityValue = Math.abs(mVelocityTracker.getXVelocity()) ;
            Log.d(TAG, "onInterceptTouchEvent():  mVelocityValue = " + velocityValue);
            
            if (velocityValue > 300 && mAllowScroll) {
                return true;
            }
            
            break;
        case MotionEvent.ACTION_UP:
            Log.i(TAG, "onInterceptTouchEvent():  ACTION_UP");
            
            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
            
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.i(TAG, "onInterceptTouchEvent():  ACTION_CANCEL");
            break;
        default:
            break;
        }
        
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent()");

        float x = event.getX();
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            Log.i(TAG, "onTouchEvent():  ACTION_DOWN");
            mFinished = mScroller.isFinished();
            if(!mFinished){
                return false;
            }
            break;

        case MotionEvent.ACTION_MOVE:
            Log.i(TAG, "onTouchEvent():  ACTION_MOVE");
            getChildAt(1).scrollTo(-(int)x, 0);
            break;

        case MotionEvent.ACTION_UP:
            Log.i(TAG, "onTouchEvent():  ACTION_UP");
            
            if(!mAllowScroll){
                break;
            }
            
           float width = getWidth();
           // 响应滚动子View的临界值,若觉得响应过于灵敏,可以将只改大些。
           // 比如:criticalWidth = width / 3或criticalWidth = width / 2,看情况而定,呵呵。
           float criticalWidth = width / 5;
           
           Log.i(TAG, "onTouchEvent():  ACTION_UP x = " + x + "\t criticalWidth = " + criticalWidth);
           
           int scrollX = getChildAt(1).getScrollX();
           
           if ( x < criticalWidth) {
               Log.i(TAG, "onTouchEvent():  ACTION_UP 向左滑动");
               
                mPanelInvisible = false;
               
                mScroller.startScroll(scrollX, 0, -scrollX, 0, ANIMATION_DURATION_TIME);
                invalidate();
            } else if ( x > criticalWidth){
                Log.i(TAG, "onTouchEvent():  ACTION_UP 向右滑动");
              
                mPanelInvisible = true;
                
                int toX = (int)(width - Math.abs(scrollX) - mHandlebarWidth);
                mScroller.startScroll(scrollX, 0, -toX, 0, ANIMATION_DURATION_TIME);
                invalidate();
            }
            
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.i(TAG, "onTouchEvent():  ACTION_CANCEL");
            break;
        default:
            break;
        }
        
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        // super.computeScroll();
        
        if(mScroller.computeScrollOffset()){
            this.getChildAt(1).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            this.postInvalidate();
        }
    }

    /**
     * 向右滑动View,让左侧操作面饭可见
     */
    public void slideToRight() {
        mFinished = mScroller.isFinished();
        if(mFinished && !mPanelInvisible){
            mPanelInvisible = true;
            
            float width = getWidth();
            int scrollX = getChildAt(1).getScrollX();
            int toX = (int)(width - Math.abs(scrollX) - mHandlebarWidth);
            
            mScroller.startScroll(scrollX, 0, -toX, 0, ANIMATION_DURATION_TIME);
            invalidate();
        }
    }
    
    /**
     * View滑动事件监听器
     * @author android_ls
     */
    public interface OnSlideListener {
        /**
         * 向左滑动子View
         */
        public abstract void toLeft();
        
        /**
         * 向右滑动子View
         */
        public abstract void toRight();
    }
    
}

测试的Activity源码如下:

package com.everyone.android.ui;

import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;

import com.everyone.android.AppBaseActivity;
import com.everyone.android.widget.FreshNewsLayout;
import com.everyone.android.widget.HorizontalScrollerContainer;
import com.everyone.android.widget.HorizontalScrollerContainer.OnSlideListener;
import com.everyone.android.widget.LeftPanelLayout;

/**
 * 功能描述:应用主界面
 * @author android_ls
 *
 */
public class EveryoneActivity extends AppBaseActivity implements OnSlideListener {

    /**
     * 滚动(滑动)容器
     */
    private HorizontalScrollerContainer mSlideContainer;

    /**
     * 左侧面板
     */
    private LeftPanelLayout mLeftPanelLayout;

    /**
     * 新鲜事
     */
    private FreshNewsLayout mFreshNewsLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(mSlideContainer);
       
    }

    @Override
    protected int getLayoutId() {
        return 0;
    }

    @Override
    protected void setupView() {
        mSlideContainer = new HorizontalScrollerContainer(mContext);

        mLeftPanelLayout = new LeftPanelLayout(mContext);
        mFreshNewsLayout = new FreshNewsLayout(mContext);
        mFreshNewsLayout.setOnSlideListener(this);

        LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        mSlideContainer.addView(mLeftPanelLayout, layoutParams);
        mSlideContainer.addView(mFreshNewsLayout, layoutParams);
    }

    @Override
    protected void initialized() {
        // TODO Auto-generated method stub

    }

    @Override
    public void toLeft() {
        // TODO Auto-generated method stub

    }

    @Override
    public void toRight() {
        mSlideContainer.slideToRight();
    }

}

运行效果图:

有网友问这篇博文中的某些变量是哪来的,贴出来的代码有验证过没。这里我把AppBaseActivity再贴一次,方便大家阅读。(这个系列是连载,有看不明白请翻阅前面的博文)

package com.everyone.android;

import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;

import com.everyone.android.net.AsyncBaseRequest;
import com.everyone.android.net.DefaultThreadPool;

/**
 * 功能描述:应用中界面(Activity)的基类
 * 对原有的Activity类进行扩展
 * @author android_ls
 */
public abstract class AppBaseActivity extends Activity {

    /**
     * 当前activity所持有的所有网络请求
     */
    protected List<AsyncBaseRequest> mAsyncRequests;

    /**
     * 当前activity所持有的Handler
     */
    protected Handler mHandler;

    /**
     * 当前activity所持有的线程池对象
     */
    protected DefaultThreadPool mDefaultThreadPool;

    protected Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int layoutId = getLayoutId();
        if (layoutId != 0) {
            setContentView(getLayoutId());
        }

        mContext = this.getApplicationContext();
        ((EveryoneApplication) this.getApplication()).addActivity(this);

        mHandler = new Handler();
        mAsyncRequests = new ArrayList<AsyncBaseRequest>();
        mDefaultThreadPool = DefaultThreadPool.getInstance();

        // 初始化组件
        setupView();
        // 初始化数据
        initialized();
    }

    public List<AsyncBaseRequest> getAsyncRequests() {
        return mAsyncRequests;
    }

    public Handler getHandler() {
        return mHandler;
    }

    public DefaultThreadPool getDefaultThreadPool() {
        return mDefaultThreadPool;
    }

    public Context getContext() {
        return mContext;
    }

    /**
     * 布局文件ID
     * @return
     */
    protected abstract int getLayoutId();

    /**
     * 初始化组件
     */
    protected abstract void setupView();

    /**
     * 初始化数据
     */
    protected abstract void initialized();

    @Override
    protected void onPause() {
        /**
         * 在activity暂停的时候,同时设置终止标识,终止异步线程
         */
        cancelAllRequest();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        /**
         * 在activity销毁的时候,同时设置终止标识,终止异步线程
         */
        cancelAllRequest();
        super.onDestroy();
    }

    /**
     * 取消当前Activity相关的网络请求
     */
    private void cancelAllRequest() {
        if (mAsyncRequests != null && mAsyncRequests.size() > 0) {
            try {
                int size = mAsyncRequests.size();
                for (int i = 0; i < size; i++) {
                    AsyncBaseRequest request = mAsyncRequests.get(i);
                    Thread thread = new Thread(request);
                    if (thread.isAlive() || !Thread.interrupted()) {
                        request.setInterrupted(true);
                    }

                    HttpURLConnection conn = request.getRequestConn();
                    if (conn != null) {
                        // conn.disconnect();
                        System.err.println("onDestroy disconnect URL: " + conn.getURL());
                    }

                    mAsyncRequests.remove(request);
                    size = mAsyncRequests.size();
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /**
     * 显示Toast形式的提示信息
     * @param message
     */
    protected void show(String message) {
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
    }
    
}


分享到:
评论

相关推荐

    易语言v5.7.1[极致精简版]

    易语言v5.7.1[极致精简版]易语言v5.7.1[极致精简版]易语言v5.7.1[极致精简版]

    MONyog v5.7.1-0 MySQL Monitor Ultimate for Windows

    MONyog MySQL Monitor Ultimate v5.7.1-0,带序列号,本人亲测可用。 此前由于Webyog频繁的调整注册机制,之前的大部分Key都被封杀后,很久一段时间都用不了5.3以后的官网新版。 现在终于有可以使用的新版了,下载...

    HP-Socket v5.7.1.rar

    HP-Socket v5.7.1版本,2019年12月22日最新的资料,支持C,C++,Delphi和E语言,具体C#语言另一个文件上传,这个文件太大了,传不上去了

    Macro Recorder v5.7.1注册版.rar

    软件介绍: 注:本软件需要安装.net4.0框架,否则无法继续安装。...通过这些功能强大的命令,偿能够做也具有特别功能的脚本程序,可以用于制作游戏外G等。压缩包内附序列号,填写上后即是注册版!

    Rosetta Stone-v5.7.1_build_50701017.apk

    Rosetta Stone-v5.7.1_build_50701017.apk

    TMS_Component_pack_fullsource V5.7.1安装版

    TMS_Component_pack_fullsource安装版 V5.7.1

    Android技术内幕.系统卷(扫描版)

    《android技术内幕:系统卷》 前言 第1章 准备工作 /1 1.1 深入认识android /2 1.1.1 android的系统构架 /2 1.1.2 android的初始化流程 /5 1.1.3 各个层次之间的相互关系 /8 1.1.4 android系统开发(移植)和应用...

    MONyog v5.7.1-0 MySQL Monitor.zip

    MySQL监控工具 MONyog可以动态地监控企业数据库环境,并针对MySQL系统安全性、数据库优化以及减少停机时间等提供专家意见。

    int2e-HPSocket.Net-master_v5.7.1.zip

    HPSocket_v5.7.1版本所支持C#文件,需要的可以进行下载,目前国内号称最好的库,超级稳定的一个网络开发库,谢谢

    Access2MySQL Pro v5.7.1

    Access2MySQL 是能允许你转换.mdb((Microsoft Access 数据库)数据库为MySQL以及反之亦然的高效应用程序.不像其他只能进行单向转换的工具.除此之外,Acces2MySQL 支持数据库同步,可以避免重复数据。

    pypy2-v5.7.1-src.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、...

    node-v5.7.1.tar.gz

    Node.js,简称Node,是一个...在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    【WordPress插件】2022年最新版完整功能demo+插件v5.7.1.zip

    "【WordPress插件】2022年最新版完整功能demo+插件v5.7.1 Stylish Cost Calculator Premium 时尚成本计算器溢价" ---------- 泰森云每天更新发布最新WordPress主题、HTML主题、WordPress插件、shopify主题、...

    hue-3.9.0-cdh5.7.1

    hue-3.9.0-cdh5.7.1 源码 hue-3.9.0-cdh5.7.1 源码 hue-3.9.0-cdh5.7.1 源码

    【WordPress插件】2022年最新版完整功能demo+插件v5.7.1 Nulled.zip

    "【WordPress插件】2022年最新版完整功能demo+插件v5.7.1 Nulled ARForms: Wordpress Form Builder Plugin Arforms:WordPress Form Builder插件" ---------- 泰森云每天更新发布最新WordPress主题、HTML主题、...

    pypy2-v5.7.1-win32.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、...

    pypy3-v5.7.1-src.zip

    TensorFlow是一个开放源代码的软件库,用于进行高性能数值计算。通过其灵活的架构,它允许用户轻松地部署计算工作在各种...此外,TensorFlow支持自动微分,这对于实现复杂的机器学习算法(如深度学习网络)至关重要。

    Dev-Cpp 5.7.1

    Dev-Cpp 5.7.1为Windows环境下C/C++开发IDE 含有MinGW

    万能票据打印专家-PC V 5.7.1授权直装版

    4、支持票据项目间的公式计算:能实现录入小写金额自动转换为大写,票据各项目间能定义四则混合运算,轻松实现快速开票 5、自动填写大写日期,完全符合银行票据填规范 6、预置数据管理:用户可以定义票据开票时选择...

Global site tag (gtag.js) - Google Analytics