我们如果想在ImageView,Button,TextView等系统控件中在XML中配置自定义属性该如何实现呢?例如我们有一个scrollView,在ScrollView里面有上述的一些控件的自定义属性,实现在滑动Scrollview时,里面的控件根据滑动的距离执行各自的动画进度。scrollivew里包含的这些控件可以是任意常用的控件,如 ImageView,Button,TextView等。我们将给这些普通的系统控件配置自定义属性!看到这里是不是觉得无法实现,因为系统的ImageView,Button等是无法识别我们自定义的属性值的,系统的控件怎么识别我们随便定义的属性呢。今天我们就来解决这个问题,解决这个问题的意义在于让系统控件能像我们自定义控件一样,配置了属性就可以执行相应的动画。我们先来看一下运行效果,完整项目见:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn13

首先我们看下自定义的控件的xml布局文件:

<com.test.buderdn13.AnimatorScrollView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:discrollve="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"><com.test.buderdn13.AnimatorLinerLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:layout_width="match_parent"android:layout_height="200dp"android:background="#007788"android:fontFamily="serif"android:gravity="center"android:padding="25dp"android:text="带上您的行李箱,准备shopping!"android:textColor="@android:color/black"android:textSize="25sp"discrollve:discrollve_alpha="true" /><ImageViewandroid:layout_width="200dp"android:layout_height="120dp"android:layout_gravity="top|right"android:src="@mipmap/baggage"discrollve:discrollve_alpha="true"discrollve:discrollve_translation="fromLeft|fromBottom" /><TextViewandroid:layout_width="match_parent"android:layout_height="200dp"android:fontFamily="serif"android:gravity="center"android:padding="25dp"android:text="准备好相机,这里有你想象不到的惊喜!"android:textColor="@android:color/black"android:textSize="25sp"discrollve:discrollve_fromBgColor="#ffff00"discrollve:discrollve_toBgColor="#88EE66" /><ImageViewandroid:layout_width="220dp"android:layout_height="110dp"android:layout_gravity="right"android:src="@mipmap/camera"discrollve:discrollve_translation="fromRight" /><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="#D97C1F"android:fontFamily="serif"android:gravity="center"android:padding="20dp"android:text="这次淘宝造物节真的来了,我们都在造,你造吗?\n7月22日-7月24日\n上海世博展览馆\n在现场,我们造什么?"android:textSize="23sp"discrollve:discrollve_alpha="true"discrollve:discrollve_translation="fromBottom" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="20dp"android:src="@mipmap/sweet"discrollve:discrollve_scaleX="true"discrollve:discrollve_scaleY="true" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="20dp"android:src="@mipmap/shoes"discrollve:discrollve_alpha="true"discrollve:discrollve_scaleX="true"discrollve:discrollve_scaleY="true"discrollve:discrollve_translation="fromLeft|fromBottom" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="20dp"android:src="@mipmap/shoes"discrollve:discrollve_alpha="true"discrollve:discrollve_scaleY="true"discrollve:discrollve_translation="fromRight|fromTop" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="20dp"android:src="@mipmap/sweet"discrollve:discrollve_alpha="true"discrollve:discrollve_scaleY="true"discrollve:discrollve_translation="fromLeft" /><ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_margin="20dp"android:src="@mipmap/camera"discrollve:discrollve_scaleY="true"discrollve:discrollve_translation="fromLeft" /></com.test.buderdn13.AnimatorLinerLayout></com.test.buderdn13.AnimatorScrollView>

AnimatorScrollView(重写了Scrollview) --->AnimatorLinerLayout(重写了LinearLayout)--->包含的系统控件。

现在我们来解释下为什么要重写AnimatorScrollView与AnimatorLinerLayout以及如何让系统控件如Imageview等来识别我们的自定义属性。

1.1  为什么需要自定义AnimatorScrollView

为什么要重写AnimatorScrollView,这个是为了重写

protected void onScrollChanged(int l, int t, int oldl, int oldt)

目的是用来获取滑动的距离t的,然后t/控件的height就可以得出一个比例,执行每个内部控件的动画(透明度,平移X,平移Y等)的比例。

1.2 为什么需要AnimatorLinerLayout

这个是关键,为了解决系统控件(如Imageview)不识别我们的自定义属性的问题。

AnimatorLinerLayout继承于LinearLayout,我们自定义LinearLayout,无非是想改变LinearLayout的行为。那么想改变什么行为呢。我们想利用AnimatorLinerLayout来获取AnimatorLinerLayout包含的各个系统控件,并且解析到为系统控件配置的自定义属性值。这个我们很容易用一个for循环获取到各个子控件及相关XML自定义属性的值。但是我们获取到了这些自定义控件属性又能如何,imageview等系统控件又不识别,就不能执行动画。那我们获取这些自定义属性给谁用??

我们可以在imageview外再包裹一个自定义父布局ViewGroup,然后把这些获取到的自定义属性(动画属性值)赋予这个包裹的VIEWGROUP,然后让父布局可以根据属性值来执行动画,那么里面的imageview是不是也就跟着动起来了呢?这个想法应该可以实现,整体布局都执行动画飞了起来,子布局自然就跟着动了起来,相当于我们的系统控件(如Imageview)执行了动画。这是一个瞒天过海的做法,关于如何在LinearLayout addView之前给每一个系统控件包裹VIEWGROUP的事情,就交给了我们自定义的LinearLayout:AnimatorLinerLayout。这就是我们为什么需要AnimatorLinerLayout的原因:包裹+动画 = 子控件动画 = 瞒天过海

我们总结一下上面的分析:

1. 自定义LinearLayout:DisScrollviewContent,改变布局结构,用自定义VIEWGROUP包裹系统控件如imageview等。

2. 自定义VIEWGROUP(用于包裹)

3. 自定义Scrollview:DisScrollview,根据滑动的距离来计算动画执行的进度比例。

2.1AnimatorLinerLayout(自定义LinearLayout)

/*** 外层布局控件,总控内部*/
public class AnimatorLinerLayout extends LinearLayout {public AnimatorLinerLayout(Context context) {this(context, null);}public AnimatorLinerLayout(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public AnimatorLinerLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setOrientation(VERTICAL);}/*** 这个函数在加载XML布局时自动调用,可以获取到每一个系统控件配置的布局参数,包括自定义参数。* 每加载一个系统控件(如Imageview),则调用一次这个函数。* @param attrs* @return*/@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {从attrs所有参数里提取自定义属性值,并保持在MyLayoutParams对象里,以供“自定义包裹VIEWGROUP"使用并执行动画。return new AnimatorLayoutParams(getContext(), attrs);}//1.考虑到系统控件不识别自定义属性,所以我门要考虑给控件包一层帧//2.这里采取有父容器组件给子容器包裹一层的方式//3.系统是通过夹杂i 布局文件,然后调用VIEW的addView来进行加载的//4.那么此时我门进行偷天换日/*** 这个函数是在generateLayoutParams之后执行,在这里我们可以获取到generateLayoutParams函数返回的MyLayoutParams里的自定义属性值。* 然后在addview系统控件(如Imageview)之前,先创建并添加一个“自定义包裹VIEWGROUP"视图,然后将自定义属性赋给这个视图,最后在把系统控件* ddview到"自定义包裹VIEWGROUP"里,从而实现了在代码中为XML里的每一个系统控件外层包裹一个“自定义包裹VIEWGROUP"视图。* @param child* @param params*/@Overridepublic void addView(View child, ViewGroup.LayoutParams params) {//获取自定义属性,这时考虑到自定义属性在子控件当中,//那么系统控件不识别自定义属性,怎么让自定义属性到这个里面来//来看源码//根据源码流程-->先调用generateLayoutParams组装XML属性参数//在调用addView进行添加,所以,自定义属性在generateLayoutParams中进行组装获取//在addView当中将具体的值进行封装AnimatorLayoutParams layoutParams = (AnimatorLayoutParams) params;AnimatorFramelayout view = new AnimatorFramelayout(child.getContext());if (!isDiscrollvable(layoutParams)) {//没有自定义属性的系统控件,我们就不需要外层包裹一个“自定义包裹VIEWGROUP"视图。直接addview即可。super.addView(view);} else {//有自定义属性的系统控件,我们需要外层包裹一个“自定义包裹VIEWGROUP"视图。view.addView(child);view.setmDiscrollveAlpha(layoutParams.mDiscrollveAlpha);view.setmDiscrollveFromBgColor(layoutParams.mDiscrollveFromBgColor);view.setmDiscrollveToBgColor(layoutParams.mDiscrollveToBgColor);view.setmDiscrollveScaleX(layoutParams.mDiscrollveScaleX);view.setmDiscrollveScaleX(layoutParams.mDiscrollveScaleY);view.setmDisCrollveTranslation(layoutParams.mDisCrollveTranslation);super.addView(view, params);}//至此到这一步就已经获取到了自己的自定义属性,可以进行操作了}private boolean isDiscrollvable(AnimatorLayoutParams layoutParams) {return layoutParams.mDiscrollveAlpha ||layoutParams.mDiscrollveScaleX ||layoutParams.mDiscrollveScaleY ||layoutParams.mDisCrollveTranslation != -1 ||(layoutParams.mDiscrollveFromBgColor != -1 &&layoutParams.mDiscrollveToBgColor != -1);}/*** 自定义LayoutParams* 获取自定义属性*/private class AnimatorLayoutParams extends LinearLayout.LayoutParams {private boolean mDiscrollveAlpha;private boolean mDiscrollveScaleX;private boolean mDiscrollveScaleY;private int mDisCrollveTranslation;private int mDiscrollveFromBgColor;private int mDiscrollveToBgColor;private AnimatorLayoutParams(Context c, AttributeSet attrs) {super(c, attrs);TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DiscrollView_LayoutParams);//没有传属性过来,给默认值FALSEmDiscrollveAlpha = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_alpha, false);mDiscrollveScaleX = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleX, false);mDiscrollveScaleY = a.getBoolean(R.styleable.DiscrollView_LayoutParams_discrollve_scaleY, false);mDisCrollveTranslation = a.getInt(R.styleable.DiscrollView_LayoutParams_discrollve_translation, -1);mDiscrollveFromBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_fromBgColor, -1);mDiscrollveToBgColor = a.getColor(R.styleable.DiscrollView_LayoutParams_discrollve_toBgColor, -1);a.recycle();}}
}

想要系统获取我们自定义的属性,关键就是重写这两个函数:

generateLayoutParams:这个函数在加载XML布局时自动调用,可以获取到每一个系统控件配置的布局参数,包括自定义参数。每加载一个系统控件(如Imageview),则调用一次这个函数。

addView:这个函数是在generateLayoutParams之后执行,在这里我们可以获取到generateLayoutParams函数返回的MyLayoutParams里的自定义属性值。在addview系统控件(如Imageview)之前,先创建并添加一个“自定义包裹VIEWGROUP"视图,然后将自定义属性赋给这个视图,最后在把系统控件addview到"自定义包裹VIEWGROUP"里,从而实现了在代码中为XML里的每一个系统控件外层包裹一个“自定义包裹VIEWGROUP"视图。

OK,至此我们已经实现了在系统控件外包裹一层可以识别自定义属性的VIEWGROUP父布局,接下来我们就来看一下这个自定义VIEWGROUP是如何执行动画的。

2.1 自定义VIEWGROUP:  AnimatorFramelayout

public class AnimatorFramelayout extends FrameLayout implements DiscrollInterface {public AnimatorFramelayout(@NonNull Context context) {this(context, null);}public AnimatorFramelayout(@NonNull Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public AnimatorFramelayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}/*** <attr name="discrollve_translation">* <flag name="fromTop" value="0x01" />* <flag name="fromBottom" value="0x02" />* <flag name="fromLeft" value="0x04" />* <flag name="fromRight" value="0x08" />* </attr>* 0000000001* 0000000010* 0000000100* 0000001000* top|left* 0000000001 top* 0000000100 left 或运算 |* 0000000101* 反过来就使用& 与运算*///保存自定义属性//定义很多的自定义属性private static final int TRANSLATION_FROM_TOP = 0x01;private static final int TRANSLATION_FROM_BOTTOM = 0x02;private static final int TRANSLATION_FROM_LEFT = 0x04;private static final int TRANSLATION_FROM_RIGHT = 0x08;//颜色估值器private static ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();/*** 自定义属性的一些接收的变量*/private int mDiscrollveFromBgColor;//背景颜色变化开始值private int mDiscrollveToBgColor;//背景颜色变化结束值private boolean mDiscrollveAlpha;//是否需要透明度动画private int mDisCrollveTranslation;//平移值private boolean mDiscrollveScaleX;//是否需要x轴方向缩放private boolean mDiscrollveScaleY;//是否需要y轴方向缩放private int mHeight;//本view的高度private int mWidth;//宽度public void setmDiscrollveFromBgColor(int mDiscrollveFromBgColor) {this.mDiscrollveFromBgColor = mDiscrollveFromBgColor;}public void setmDiscrollveToBgColor(int mDiscrollveToBgColor) {this.mDiscrollveToBgColor = mDiscrollveToBgColor;}public void setmDiscrollveAlpha(boolean mDiscrollveAlpha) {this.mDiscrollveAlpha = mDiscrollveAlpha;}public void setmDisCrollveTranslation(int mDisCrollveTranslation) {this.mDisCrollveTranslation = mDisCrollveTranslation;}public void setmDiscrollveScaleX(boolean mDiscrollveScaleX) {this.mDiscrollveScaleX = mDiscrollveScaleX;}public void setmDiscrollveScaleY(boolean mDiscrollveScaleY) {this.mDiscrollveScaleY = mDiscrollveScaleY;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;}@Overridepublic void onDiscroll(float ratio) {//执行动画ratio:0~1if (mDiscrollveAlpha) {setAlpha(ratio);}if (mDiscrollveScaleX) {setScaleX(ratio);}if (mDiscrollveScaleY) {setScaleY(ratio);}//平移动画  int值:left,right,top,bottom    left|bottomif (isTranslationFrom(TRANSLATION_FROM_BOTTOM)) {//是否包含bottomsetTranslationY(mHeight * (1 - ratio));//height--->0(0代表恢复到原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_TOP)) {//是否包含bottomsetTranslationY(-mHeight * (1 - ratio));//-height--->0(0代表恢复到原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_LEFT)) {setTranslationX(-mWidth * (1 - ratio));//mWidth--->0(0代表恢复到本来原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_RIGHT)) {setTranslationX(mWidth * (1 - ratio));//-mWidth--->0(0代表恢复到本来原来的位置)}//判断从什么颜色到什么颜色if (mDiscrollveFromBgColor != -1 && mDiscrollveToBgColor != -1) {setBackgroundColor((int) sArgbEvaluator.evaluate(ratio, mDiscrollveFromBgColor, mDiscrollveToBgColor));}}@Overridepublic void onResetDiscroll() {if (mDiscrollveAlpha) {setAlpha(0);}if (mDiscrollveScaleX) {setScaleX(0);}if (mDiscrollveScaleY) {setScaleY(0);}//平移动画  int值:left,right,top,bottom    left|bottomif (isTranslationFrom(TRANSLATION_FROM_BOTTOM)) {//是否包含bottomsetTranslationY(mHeight);//height--->0(0代表恢复到原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_TOP)) {//是否包含bottomsetTranslationY(-mHeight);//-height--->0(0代表恢复到原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_LEFT)) {setTranslationX(-mWidth);//mWidth--->0(0代表恢复到本来原来的位置)}if (isTranslationFrom(TRANSLATION_FROM_RIGHT)) {setTranslationX(mWidth);//-mWidth--->0(0代表恢复到本来原来的位置)}}private boolean isTranslationFrom(int translationMask) {if (mDisCrollveTranslation == -1) {return false;}//fromLeft|fromeBottom & fromBottom = fromBottomreturn (mDisCrollveTranslation & translationMask) == translationMask;}}

我们发现自定义控件里实现了接口DiscrollvableInterface并重写了

void onDiscrollve(float ratio)  //根据比例,执行动画进度

void onResetDiscrollve();//逆向动画,恢复到初始状态。

在这两个函数里会根据自定义属性值与ratio来执行 这个“自定义VIEWGROUP包裹”的动画,从而内部包含的系统控件(如Imageview)等也会跟着动起来。那这两个函数是在什么地方调用的,以及ratio是怎么算出来的,那这个与Scrollview的滑动有关系。那我们就来看一下自定义Scrollview。

2.2自定义Scrollview

public class AnimatorScrollView extends ScrollView {private AnimatorLinerLayout mContent;public AnimatorScrollView(Context context) {super(context);}public AnimatorScrollView(Context context, AttributeSet attrs) {super(context, attrs);}public AnimatorScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}//渲染完毕之后@Overrideprotected void onFinishInflate() {super.onFinishInflate();mContent = (AnimatorLinerLayout) getChildAt(0);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);View first = mContent.getChildAt(0);first.getLayoutParams().height = getHeight();}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {//监听滑动程度--CHILD从下面冒出来多少距离//需要一个百分比来执行动画,//百分比为  滑出高度/child实际高度=百分比//实际高度int scrollViewHeight = getHeight();//监听滑动的程度---childView从下面冒出来多少距离/childView.getHeight();----0~1:动画执行的百分比ratio//动画执行的百分比ratio控制动画执行for (int i = 0; i < mContent.getChildCount(); i++) {View child = mContent.getChildAt(i);int childHeight = child.getHeight();if (!(child instanceof DiscrollInterface)) {continue;}//接口回掉,传递执行的百分比给MyFrameLayout//低耦合高内聚DiscrollInterface discrollInterface = (DiscrollInterface) child;//child离parent顶部的高度int childTop = child.getTop();//滑出去的这一截高度:t
//            child离屏幕顶部的高度int absoluteTop = childTop - t;if (absoluteTop <= scrollViewHeight) {//child浮现的高度 = ScrollView的高度 - child离屏幕顶部的高度int visibleGap = scrollViewHeight - absoluteTop;//float ratio = child浮现的高度/child的高度float ratio = visibleGap / (float) childHeight;//确保ratio是在0~1的范围discrollInterface.onDiscroll(clamp(ratio, 1f, 0f));} else {discrollInterface.onResetDiscroll();}}}/*** 求中间大小*/private float clamp(float value, float max, float min) {return Math.max(Math.min(value, max), min);}
}

K,所有流程就分析完了。现在总结一下思路:

1. 自定义LinearLayout的addview,让添加imageview等系统控件前,先在外层包裹一个自定义VIEWGROUP,并赋予它自定义属性的配置。

2. 自定义VIEWGROUP,接收滑动的ratio来执行动画进度

3. 自定义Scrollview,计算滑动的ratio,并调用自定义VIEWGROUP里的执行动画函数。

完整项目

Android系统控件获取自定义属性相关推荐

  1. 腾讯Android自动化测试实战3.1.4 Robotium的控件获取、操作及断言

    3.1.4 Robotium的控件获取.操作及断言 Robotium是一款在Android客户端中的自动化测试框架,它需要模拟用户操作手机屏幕.要完成对手机的模拟操作,应该包含以下几个基本操作: (1 ...

  2. Android UI 统一修改Button控件的样式,以及其它系统控件的默认样式

    先介绍下修改原理:首先打开位于android.widget包下面的Button.java文件,这里有一句关键的代码如下: public Button(Context context, Attribut ...

  3. Android游戏开发系统控件-CheckBox

    Android游戏开发系统控件-CheckBox 2012/5/11 星期五 CheckBox是Android系统最普通的UI控件,继承了Button按钮 下面通过一个实例来学习 作者:wwj 功能: ...

  4. Android游戏开发系统控件-Dialog

    Android游戏开发系统控件-Dialog Dialog(对话框)在Android应用开发中经常用到,下面是学习<Android游戏编程从零开始>一书,关于Dialog的初步学习. 创建 ...

  5. android layout 替换,LayoutInflater 后记--替换系统控件

    之前记录了自己走通 LayoutInflater 的源码整体流程,一直想搞个好玩的东西,想起之前看到过的换肤方案,决定写个换系统控件的库. 项目地址,项目的具体使用,可以看README 首先需要确定, ...

  6. android 获取控件 id 工具,如何使用appium desktop 获取Android APP 控件的id

    背景: 随着Android 版本的更新,你会发现我们之前用的最新的版本的Appium 1.5.3无法启动我们的 Android APP 在模拟器为7.0的设备上,所以我们有必要了解如何使用 appiu ...

  7. Android自定义AlertDialog的控件获取操作

    Android自定义AlertDialog的控件获取操作 在自定义的AlertDialog布局虽然可以显示,但是试过很多方法都不能获得其中的控件进行操作,找了很多方法最后这种方法可以. dialog的 ...

  8. Android学习--02(猜猜我的星座App源码+Android常用控件TextView+EditText+Button+ImangeView+DatePicker+App间通信+跳转页面)

    猜猜我的星座App 1 Android常用控件 1.1 TextView控件 1.1.1 简介 1.1.2属性 1.1.3 扩展属性 1.1.4 TextView的使用方法 1.1.5总结 1.2 E ...

  9. Android学习|控件——Notification通知

    Android学习|控件--Notification通知 一.前提 二.两个对象的的构建 1.创建NotificationManager 2.使用Builder构造器来创建Notification 2 ...

最新文章

  1. java中如果写设置密码,在java中编写一个多字母换位密码[关闭]
  2. TFS 2012研发管理能力(9)
  3. Perl语言编程学习笔记2
  4. 错误: 找不到或无法加载主类 org.apache.hadoop.hbase.util.GetJavaProperty
  5. 2018.03.12、Android知识点-Java篇
  6. 再见2019,拥抱2020
  7. 《哪吒之魔童降世》电影密钥延期至9月26日,将冲击中国票房总榜前三
  8. element提交图片限制一张_element-ui上传图片限制图片比例
  9. 使用索引的技巧知识点
  10. 基于VISSIM和Python的二次开发基础—多时段配时控制
  11. android http统一回调,Android使用OKHttp构建带进度回调的多文件下载器
  12. STM32 硬件I2C读写AT24C02/08
  13. Go语言自学系列 | 安装Golang
  14. 关于table表格头部固定和列固定的方式
  15. rar压缩包密码解密工具
  16. BOM成本计算成最下级算到最上级
  17. WinMerge使用
  18. 【我的世界】自定义局域网服务器-LanServerPropertie-1.17.x-自定义端口+关正版验证
  19. web项目上云_联想Filez—携手浙江中烟,发力“云”端,打造“烟草上云”新势能...
  20. context.getContentResolver().query()详细用法详解

热门文章

  1. Wavefront公司的.obj文件格式及.mtl文件格式
  2. [转]【C/C++】STL详解
  3. 网络系统集成实习——第一天——2017.9.6
  4. 机器学习的三大主要任务
  5. 经验 | 训练多任务学习(Multi-task Learning)方法总结
  6. (附源码)springboot电影院售票与管理系统 毕业设计011449
  7. VMware+物理机——搭建内外网环境
  8. elasticsearch7.1.1入门之集群的基础配置
  9. 2022年全球及中国血清降钙素原市场专项调研与竞争调查分析报告
  10. 简单图像滤镜功能的Java实现