Android自定义Spinner的例子

 更新时间:2016年9月20日 19:54  点击:1589
下文是一个Android自定义Spinner的例子,我们下面就一起来看看这篇文章希望能够让各位深入的理解Spinner的用法,具体例子如下所示。


最近在做的项目中有很多下拉框,为了实现方便就用了Android 自带的Spinner,但是自带的Spinner的样式又不符合要求,就学习了一下自定义Spinner。下面是整个步骤:

1.准备好图片

2.style中定义

 
<!-- spinner -->
<style name="spinner_style">
 <item name="android:background">@drawable/spinner</item>
<item name="android:paddingLeft">5dip</item>

 

3.调用

<Spinner

android:id="@+id/field_item_spinner_content"

style="@style/spinner_style"

android:layout_width="fill_parent"

android:layout_height="wrap_content"/>

 

4.在layout中定义simple_spinner_item.xml

<?xml version="1.0" encoding="utf-8"?>

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@android:id/text1"

android:paddingLeft="5dip"

android:paddingRight="5dip"

android:gravity="center_vertical"

android:textColor="#808080"

android:singleLine="true"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

 

5.java代码

ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,R.layout.simple_spinner_item);

String level[] = getResources().getStringArray(R.array.affair_level);//资源文件

for (int i = 0; i < level.length; i++) {

    adapter.add(level[i]);

      }

adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

spinner.setAdapter(adapter);

效果图:

 

Screenshot_2015-08-20-07-17-28

本文主要 介绍Android 开发中的两个UI控件RecyclerView,CardView,及这两个控件的导入导出功能。

先上效果图:


原理图:


这是RecycleView的工作原理:
1.LayoutManager用来处理RecycleView的“列表”样式,Support包默认包含了:LinearLayoutManager  横向或纵向的滚动列表、
GridLayoutManager  网格列表、StaggeredGridLayoutManager  交错的网格列表。
2.Adapter负责处理RecycleView的数据和样式
3.在传统的ListView中有一种常见的写法是使用ViewHolder来缓存数据集,在新版的RecycleView内置了ViewHolder这一模块,所以在Adapter内部新建内部类ViewHolder。
4.RecycleView 和listView的一个区别就是本身不处理点击事件,点击事件应该绑在ViewHolder里面,可以直接写也可以通过接口绑在Adapter里面来实现。

 
首先添加数据集:

package com.lfk.drawapictiure.Info;
/**
 * Created by liufengkai on 15/9/13.
 */
public class MenuInfo {
    private String paint_name;
    private String paint_time;
    private String paint_root;
    private String paint_img_root;
    public MenuInfo(String paint_name, String paint_time,
                    String paint_root, String paint_img_root) {
        this.paint_name = paint_name;
        this.paint_time = paint_time;
        this.paint_root = paint_root;
        this.paint_img_root = paint_img_root;
    }
    public String getPaint_name() {
        return paint_name;
    }
    public String getPaint_time() {
        return paint_time;
    }
    public String getPaint_root() {
        return paint_root;
    }
    public String getPaint_img_root() {
        return paint_img_root;
    }
}

 
实现继承自RecycleView的Adapter中间要包裹自己实现的ViewHolder,onCreateviewHolder函数和onBindViewHolder实现了ListView里面getView的工作,分别为找到控件和控件赋值,

实现点击的接口,设置接口并且绑在ViewHolder的itemView里面即根视图中。

public class MainLayoutAdapter extends RecyclerView.Adapter<MainLayoutAdapter.MainViewHolder> {
    private LayoutInflater inflater;
    private ArrayList<MenuInfo> userList;
    private Context context;
    private MainItemClickListener itemClickListener;
    public MainLayoutAdapter(ArrayList<MenuInfo> userList, Context context) {
        this.userList = userList;
        this.context = context;
        this.inflater = LayoutInflater.from(context);
    }
    public void setItemClickListener(MainItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }
  //onCreateviewHolder函数和onBindViewHolder实现了ListView里面getView的工作,分别为找到控件和控件赋值
    @Override
    public MainLayoutAdapter.MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View wrapper = inflater.inflate(R.layout.draw_item, parent, false);
        return new MainViewHolder(
                wrapper,
                (TextView)wrapper.findViewById(R.id.paint_name),
                (TextView)wrapper.findViewById(R.id.paint_time),
                (TextView)wrapper.findViewById(R.id.paint_root),
                (ImageView)wrapper.findViewById(R.id.paint_img));
    }
    @Override
    public void onBindViewHolder(MainViewHolder holder, int position) {
        MenuInfo menuInfo = userList.get(position);
        holder.paint_img.setImageURI(Uri.parse(menuInfo.getPaint_img_root()));
        holder.paint_name.setText(menuInfo.getPaint_name());
        holder.paint_time.setText(menuInfo.getPaint_time());
        holder.paint_root.setText(menuInfo.getPaint_root());
    }
    @Override
    public int getItemCount() {
        return userList.size();
    }
    public class MainViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{
        private TextView paint_name;
        private TextView paint_time;
        private TextView paint_root;
        private ImageView paint_img;
        public MainViewHolder(View itemView, TextView paint_name,
                          TextView paint_time, TextView paint_root,
                          ImageView paint_img) {
            super(itemView);
            itemView.setOnClickListener(this);
            this.paint_name = paint_name;
            this.paint_time = paint_time;
            this.paint_root = paint_root;
            this.paint_img = paint_img;
        }
        @Override
        public void onClick(View view) {
            MenuInfo menuInfo = userList.get(getAdapterPosition());
            itemClickListener.onItemClick(view,menuInfo.getPaint_name(),menuInfo.getPaint_root());
        }
        @Override
        public boolean onLongClick(View view) {
            return false;
        }
    }
}


Activity中的使用:

package com.lfk.drawapictiure.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.lfk.drawapictiure.Adapter.MainLayoutAdapter;
import com.lfk.drawapictiure.Info.MenuInfo;
import com.lfk.drawapictiure.InterFace.MainItemClickListener;
import com.lfk.drawapictiure.MainActivity;
import com.lfk.drawapictiure.R;
import java.util.ArrayList;
public class PaintFragment extends android.support.v4.app.Fragment {
    private RecyclerView mRecyclerView;
    private MainLayoutAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private String path = Environment.getExternalStorageDirectory().getPath() + "/DrawAPicture";
    public static PaintFragment newInstance() {
        return new PaintFragment();
    }
    public PaintFragment() {
        // Required empty public constructor
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View wrapper = inflater.inflate(R.layout.fragment_paint, container, false);
        mRecyclerView = (RecyclerView)wrapper.findViewById(R.id.paint_recycle_view);
        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager); //绑上列表管理器
        ArrayList<MenuInfo> arrayList = new ArrayList<>();
        arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg"));
        arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg"));
        arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg"));
        arrayList.add(new MenuInfo("刘丰恺","09-13 05:28",path+"/"+"12138.lfk",path+"/"+"6826.jpg"));
        mAdapter = new MainLayoutAdapter(arrayList, getActivity());
      // 设置点击事件
        mAdapter.setItemClickListener(new MainItemClickListener() {
            @Override
            public void onItemClick(View view, String name, String path) {
                Intent intent = new Intent(getActivity(), MainActivity.class);
                intent.setData(Uri.parse(path));
                startActivity(intent);
            }
        });
      //绑定数据集
        mRecyclerView.setAdapter(mAdapter);
        return wrapper;
    }
}


其中的子布局使用了CardView:

我设置的东西只有:

card_view:cardCornerRadius="4dp"  //设定圆角半径
card_view:cardElevation="8dp"//设定阴影
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:background="@drawable/list_item_selector"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="4dp"
    card_view:
    android:layout_margin="@dimen/card_margin">
    <RelativeLayout
        android:layout_marginTop="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ImageView
            android:maxHeight="100dp"
            android:layout_marginTop="8dp"
            android:layout_below="@+id/line_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/paint_img"
            android:src="@mipmap/ic_launcher" />
        <TextView
            android:id="@+id/paint_name"
            android:textColor="#000"
            android:textSize="18sp"
            android:text="项目名"
            android:layout_alignParentRight="true"
            android:singleLine="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/paint_time"
            android:text="项目时间"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:singleLine="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <View
            android:id="@+id/line_view"
            android:background="@color/gray"
            android:layout_below="@+id/paint_time"
            android:layout_width="match_parent"
            android:layout_height="1dp"/>
        <TextView
            android:id="@+id/paint_root"
            android:visibility="invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </RelativeLayout>
</android.support.v7.widget.CardView>




Android L——RecyclerView,CardView导入和使用

这篇文章是ANDROID L——Material Design详解(UI控件)的一个补充或者说是应用实例,如果有时间建议大家稍微浏览一下上篇文章。

本文主要介绍Android L新增加的两个UI控件RecyclerView,CardView的导入和使用。
RecyclerView是ListView的升级版
CardView则是Google提供的一个卡片式视图组件


本例就是使用RecyclerView来展示多个CardView的一个小例子,先看下效果图:



导入RecyclerView,CardView


由于RecyclerView,CardView是放在support library v7包中,所以我们想要使用就必须要导包。

下面就介绍下在Eclipse和Android Studio中是如何导入这两个包的。


Eclipse:




第一步:通过SDK manager下载/更新Android Support Libraries(5.0版本最新为21)



第二步:导入CardView和RecyclerView项目(都在support v7中)

1.在Eclipse中点击Import,导入Android项目

2.导入CardView和RecycleView,路径为your sdk path\extras\android\support\v7\cardview(RecycleView则为相同目录下的recyclerview)

3.导入时记得将工程copy到本地并建议重命名,这样方便以后管理例如:



第三步:设置Library

1..将两个工程设置为Library
2..在主工程中引入这两个Library例如:



通过这三步就可以将这两个包导入进来了。



Android Studio

Android Stuido相对于Eclipse简单的多:

第一步:

首先要确保已经将Android Support Libraries升级到最新.


第二步:

打开项目中的build.gradle文件,在dependencies中添加如下代码。

dependencies {
    compile 'com.android.support:recyclerview-v7:21.+'
    compile 'com.android.support:cardview-v7:21.+'
}


第三步:

重新Build一下工程。

Build完成后就会发现这两个包就已经导入进来了



代码介绍:

主题:

首先这个黑色基调的主题是使用了Material.Dark.ActionBar样式。


设置方法:修改values-v21文件夹下styles.xml文件:

<resources>
    <style name="AppTheme" parent="android:ThemeOverlay.Material.Dark.ActionBar">
    </style>
</resources>


布局文件:

recycler_view.xml(RecyclerView布局文件):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MyActivity">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MyActivity" />
</FrameLayout>


FrameLayout里包含了RecyclerView控件


card_view.xml(CardView布局文件):

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="5dp"
    android:orientation="horizontal"
    card_view:cardBackgroundColor="@color/cardview_dark_background"
    card_view:cardCornerRadius="5dp" >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:padding="5dp" >
        <ImageView
            android:id="@+id/pic"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:scaleType="centerCrop" />
        <TextView
            android:clickable="true"
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="10dp"
            android:layout_marginRight="10dp"
            android:gravity="right|bottom"
            android:textColor="@android:color/white"
            android:textSize="24sp" />
    </RelativeLayout>
</android.support.v7.widget.CardView>


CardView视图中包含了一个ImageView和一个TextView分别显示图片和文字信息


唯一需要介绍的就是在布局文件中使用了,如下两个属性:

 card_view:cardBackgroundColor="@color/cardview_dark_background"
    card_view:cardCornerRadius="5dp"

他俩的作用分别是设置CardView的背景颜色和外围的圆角大小(注意要使用card_view命名空间)


代码:

Actor类(封装数据的Model类):

public class Actor
{
    String name;
    String picName;
    public Actor(String name, String picName)
    {
        this.name = name;
        this.picName = picName;
    }
    public int getImageResourceId( Context context )
    {
        try
        {
            return context.getResources().getIdentifier(this.picName, "drawable", context.getPackageName());
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return -1;
        }
    }
}


封装了演员的名字和图片名,getImageResourceId()方法的作用就是根据图片命找到系统资源

MyActivity(程序主控制Activity)

public class MyActivity
    extends Activity
{
    private RecyclerView mRecyclerView;
    private MyAdapter myAdapter;
    private List<Actor> actors = new ArrayList<Actor>();
    private String[] names = { "朱茵", "张柏芝", "张敏", "巩俐", "黄圣依", "赵薇", "莫文蔚", "如花" };
    private String[] pics = { "p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8" };
    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycler_view);
        actors.add(new Actor("朱茵", "p1"));
        getActionBar().setTitle("那些年我们追的星女郎");
        // 拿到RecyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.list);
        // 设置LinearLayoutManager
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        // 设置ItemAnimator
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        // 设置固定大小
        mRecyclerView.setHasFixedSize(true);
        // 初始化自定义的适配器
        myAdapter = new MyAdapter(this, actors);
        // 为mRecyclerView设置适配器
        mRecyclerView.setAdapter(myAdapter);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
            // 当点击actionbar上的添加按钮时,向adapter中添加一个新数据并通知刷新
            case R.id.action_add:
                if (myAdapter.getItemCount() != names.length) {
                    actors.add(new Actor(names[myAdapter.getItemCount()], pics[myAdapter.getItemCount()]));
                    mRecyclerView.scrollToPosition(myAdapter.getItemCount() - 1);
                    myAdapter.notifyDataSetChanged();
                }
                return true;
            // 当点击actionbar上的删除按钮时,向adapter中移除最后一个数据并通知刷新
            case R.id.action_remove:
                if (myAdapter.getItemCount() != 0) {
                    actors.remove(myAdapter.getItemCount()-1);
                    mRecyclerView.scrollToPosition(myAdapter.getItemCount() - 1);
                    myAdapter.notifyDataSetChanged();
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}



MyAdapter(自定义适配器类)

public class MyAdapter
    extends RecyclerView.Adapter<MyAdapter.ViewHolder>
{
    private List<Actor> actors;
    private Context mContext;
    public MyAdapter( Context context , List<Actor> actors)
    {
        this.mContext = context;
        this.actors = actors;
    }
    @Override
    public ViewHolder onCreateViewHolder( ViewGroup viewGroup, int i )
    {
        // 给ViewHolder设置布局文件
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_view, viewGroup, false);
        return new ViewHolder(v);
    }
    @Override
    public void onBindViewHolder( ViewHolder viewHolder, int i )
    {
        // 给ViewHolder设置元素
        Actor p = actors.get(i);
        viewHolder.mTextView.setText(p.name);
        viewHolder.mImageView.setImageDrawable(mContext.getDrawable(p.getImageResourceId(mContext)));
    }
    @Override
    public int getItemCount()
    {
        // 返回数据总数
        return actors == null ? 0 : actors.size();
    }
    // 重写的自定义ViewHolder
    public static class ViewHolder
        extends RecyclerView.ViewHolder
    {
        public TextView mTextView;
        public ImageView mImageView;
        public ViewHolder( View v )
        {
            super(v);
            mTextView = (TextView) v.findViewById(R.id.name);
            mImageView = (ImageView) v.findViewById(R.id.pic);
        }
    }
}


所有代码介绍完毕了,可以总结为以下两点:

RecyclerView:

理解为之前的ListView,不过需要设置LinearLayoutManager(目前资料不多我也有点迷糊以后再补充)和ItemAnimator(为每个条目设置操作动画)两个新属性

RecyclerView.Adapter:

理解为默认自带和基于ViewHolder的新的适配器,只不过回调方法稍有不同,但本质都是一样的。

android studio 在安卓虚拟机运行的过程出现中文乱码问题肯定是我们配置有问题了,我们下面来为各位介绍这个问题的解决 办法,具体如下。

 

android乱码教程.png

 

如果在运行的时候,出现这样的问题

 

以下就是解决方法

 

File->Settings..->Editor->File Encodings->

 

android乱码教程2.png

QQ图片20150901105342.jpg

最后重启一下 android studio ,原来的中文在代码里面变成乱码了,把乱码改成中文就OK了

 

QQ图片20150901105820.png

 

改成

 

QQ图片20150901105951.png

 

保存后,打开安卓虚拟机重新运行一下。

 

QQ图片20150901110116.png

 

大功告成
下文小编为各位整理一篇关于Android程序的反编译的过程分析例子,希望例子能够对各位有所帮助。

 

一、前言

 

对抗反编译是指让apk文件或者dex文件无法正常通过反编译工具,而且有可能导致工具异常或者崩溃,如apktool、baksmali、dex2jar、JEB等等工具,如下图dex2jar无法正常工作。

 

 

二、Dex文件格式解析

 

目前大多数android软件的反编译工具都是开源的,比如apktool、Dex2jar、baksamli,大家可以非常方便的从github下载并源阅读代码,然后找到可以利用的点,再在自己的软件中加入干扰代码,让反编译工具出现异常或者无法正常阅读代码。

 

接下来让我们先来熟悉一下dex文件格式 ,一个dex文件由以下几个部份组成:

1.     Dex Header:    Dex结构头它指定了dex文件的一些基本属性,并记录了部份数据表在dex文件中的物理偏移。

2.     String Table:  字符串表,存储字符串的索引和个数

3.     Type Table:   类型表,存储类型的索引和个数

4.     Proto Table:  函数元型表,存储函数元型索引和个数

5.     Field Table:  字段表,存储字段索引和个数

6.     Method Table:  方法表,存储方法索引和个数

7.     Class def Table:类定义表,存储类定义索引和个数

8.     Data Section:  存储数据,由以上类型的索引查找,

 

 

有兴趣的可以直接翻看android的源码或者参考以下链接:

http://www.netmite.com/android/mydroid/dalvik/docs/dex-format.html

 

既然要在代码中添加干扰指令,接下的DexClassDef结构,肯定是要了解的非常清楚。

 

结构中包含了类的类型偏移、访问标志、父类类型索引、接口偏移、注释、静态类型的偏移信息,整体结构图定义如下:

 

struct DexClassDef

{  

   u4 classIdx;    

   u4 accessFlags;

   u4 superclassIdx; 

   u4 interfacesOff;  

   u4 sourcefileIdx; 

   u4 annotationsOff;  

   u4 classDataOff;    

   u4 staticValuesOff;

}

 

classIdx    字段是一个索引值,类的类型,做为下标索引在DexTypeID结构列表中查找
accessFlags   字段是类的访问标志,以ACC_开头的枚举值
superclassIdx  字段是父类的类型,做为下标索引在DexTypeID结构列表中查找
interfacesOff  字段是接口类型,做为下标索引在DexTypeList结构列表中查找
sourcefileIdx  字段是源文件名,做为下标索引在DexTypeList结构列表中查找
annotationsOff 字段是注释信息的偏移,指向DexAnnotationsDirectoryItem结构
classdataOff  字段是指向了DexClassData结构体的偏移
staticValuesOff 字段是指向DexEncodeArray结构体的偏移,记录静态数据的信息
DexClassData结构说明:

struct DexClassData

{

   DexClassDataHeader header;

   DexField*  staticFields;     //静态字段,DexField结构

   DexField*  instanceFields;   //实例字段,DexField结构

   DexMethod* directMethods;    //直接方法,DexMethod结构

   DexMethod* virtualMethods;   //虚方法,  DexMethod结构

}


DexClassData结构中的DexMethod类型描述了:方法的原型、名称、访问标志以及代码数据块,codeOff字段指向了一个DexCode结构,它描述了方法更详细的信息以及方法中指令的内容。

DexMethod结构声明如下

struct DexMethod

{  

u4 methodIdx;  //指向DexMethodId的索引

   u4 accessFlags;//访问标志

u4 codeOff;    //指向dexCode的结构偏移

}

struct DexCode

{

  ushort  registerSsize;//使用的寄存器的数目

  ushort  insSize;      //传入参数的数目

  unshort outsSize;     //调用其他方法时使用的寄存器个数

  unshort triesSize;    //try/catch异常块个数

  uint    debugInfoOff; //调试信息的偏移

  uint    insnsSize;    //指令集个数

  ushort  insns[1];     //指令集数组,变长数组

}


DexClassData树型结构图:

 

 

三、调试dex2jar工程

 

1. 将dex2jar源码导入IntelliJ IDEA,导入后IntelliJ IDEA会自动查找对应 Grable并下载,需要比较长的时间等待

 

源码地址: https://github.com/pxb1988/dex2jar

 

2.  选中Grable中的dex2jar/dex2jar/Tasks/other/antlr2java进行编译

 

 

3. 点击工具栏的 Project Structure, 然后选择Modules -> d2j-smali -> build,然后点击Excluded

 

 

4. 选择 build/generated-sources/antlr, 然后点击 Sources

 

 

5. 最后打开Excluded Gradle Task弹出对话框,运行clean distZip 命令

 

 

执行完Gradl clean distZip命令后会在\dex2jar-2.x\dex-tools\build\distributions目录下生成 dex-tools-2.1-SNAPSHOT.zip,压缩包内是编译完后生成的jar文件和一些配置信息文件。大家请参考dex2jar.sh文件,它向我们说明需要使用lib目录下的所有jar文件和入口函数com.googlecode.dex2jar.tools.Dex2jarCmd 才能将dex文件转换成jar文件

 

 

6.将dex文件转换成jar文件需要执行转换命令

 

格式如下:

java -Xms512m -Xmx1024m -classpath  .\lib\*.jar &ldquo;com.googlecode.dex2jar.tools.Dex2jarCmd&rdquo; classdex.dex

 

根据以上的命令,来配置调试参数,设置如下:

 

 

7.设置完成后,就可以开始调试

 

 

四、dex2jar反编译失败原因分析

 

1.首先我们从解包失败的错误异常入手,定位崩溃处的代码。

 

 

通过错误提示可以定位到dex2jar崩溃处的代码,源码位置如下:

dex-ir\src\main\java\com\googlecode\dex2jar\ir\TypeClass.java

 

2. 在崩溃的函数处下断点,开始调试。

 

 

a)通过抛出的异常的说明&ldquo;cant not merge I and Z&rdquo;,以及整个调用堆栈。

 

我们可以看出造成这个异常的原因是函数调用时,实参的类型和形参类型不匹配引起的。因为在Java语法中,将实参是布尔类型传递给形参是int类型。这样是不合法的,所以导致dex2jar在检查的参数类型的时候失败。

 

b)知道了崩溃的原因,那我们就需要确定它具体是使用怎样的方法做到的,通过dex2jar生成的error异常信息详细说明文件得知该dex文件中的每一个函数头部都加了一句这样的代码Exit.b(Exit.a())请看Ida,现在大致能猜到混淆者所做的工作:

 

1.首先解析dex文件格式,定位到DexCode结构中的insns成员

 

2.向代码中添加一个类成员对象名字叫Exit,然后添加代码Exit.b(Exit.a())

 

 

3.明白了怎么添加干扰代码,接下来分析一下dex2jar的工作流程,dex2jar转换成jar文件这个过程中会验证,函数调用时的形参和实参的合法性,请看下图,解析到INVOKE_开头的指令时,会开始解析返回值,供给参数,和实参等信息,具体逻辑和代码大家有兴趣可以详细地研究一下dex2jar源码。

 

 

4.merge负责的工作是参数类型的验证,如果两个类型相同,返回形参的类型,形参为UNKONW返回实参,实参为UNKONW返回形参,两个类型都不相同,则匹配异常

 

 

5.最后在dex2jar里添加代码,使dex文件能正确的被反编译成功。这里给出一段可以成功让dex2jar反编译的出jar文件的代码(其实方法有很多)

 

 

下图是成功反编译的jar文件

 

 

本教程我们主要学习在Android开发中ExecutorService线程池的应用,如何创建线程池;调用线程池的方法,获取线程执行完毕后的结果;关闭线程等。

首先我们先了解一下到底什么是线程池,只有了解了其中的道理,我们才能够进行应用...java.util.concurrent.ExecutorService表述了异步执行的机制

首先我们简单的举一个例子...

package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("cc");
        ExecutorService executorService=Executors.newFixedThreadPool(10);
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        System.out.println("bb");
    }
}


这里我们指定了十个线程处于一个线程池内部,线程池的原理其实就是对多线程的一个管理,为了实现异步机制的一种方法,其实就是多个线程执行多个任务,最终这些线程通过线程池进行管理...不用手动去维护...一次可以处理多个任务,这样就可以迅速的进行相应...比如说一个网站成为了热点网站,那么对于大量的点击量,就必须要对每一次的点击做出迅速的处理,这样才能达到更好的交互效果...这样就需要多个线程去处理这些请求,以便能够更好的提供服务...

1. 简单的说一下如何创建线程池进行初始化....创建线程有几种常用方式...这里都是使用了Executors工厂来实例化对象,同时我们也可以根据需求自己去写一个ExecutorService...这几种常用的方法有一定的区别...

ExecutorService executorService1 = Executors.newSingleThreadExecutor();

ExecutorService executorService2 = Executors.newFixedThreadPool(10);

ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

ExecutorService executorService4 = Executors.newCacheThreadPool();

Executors.newSingleThreadExecutor()

单例线程,表示在任意的时间段内,线程池中只有一个线程在工作...

Executors.newCacheThreadPool()

缓存线程池,先查看线程池中是否有当前执行线程的缓存,如果有就resue(复用),如果没有,那么需要创建一个线程来完成当前的调用.并且这类线程池只能完成一些生存期很短的一些任务.并且这类线程池内部规定能resue(复用)的线程,空闲的时间不能超过60s,一旦超过了60s,就会被移出线程池.
Executors.newFixedThreadPool(10)     固定型线程池,和newCacheThreadPool()差不多,也能够实现resue(复用),但是这个池子规定了线程的最大数量,也就是说当池子有空闲时,那么新的任务将会在空闲线程中被执行,一旦线程池内的线程都在进行工作,那么新的任务就必须等待线程池有空闲的时候才能够进入线程池,其他的任务继续排队等待.这类池子没有规定其空闲的时间到底有多长.这一类的池子更适用于服务器.
Executors.newScheduledThreadPool(10)     

调度型线程池,调度型线程池会根据Scheduled(任务列表)进行延迟执行,或者是进行周期性的执行.适用于一些周期性的工作.

这就是线程池创建的几种方式...我们需要根据不同的需求来适当的选择到底使用哪种线程池...

2.那么创建了线程池以后就需要对线程池进行调用..将任务加载到其中...

i.ExecutorService.execute(Runnable);

第一种调用方式...通过这种方式将线程任务加载到线程池当中...我们可以添加多个任务...贴上一个完整的代码...大家看一下代码的解释就明白到底是怎么回事了..不难理解...

package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
    /**
     * @param args
     * 
     */
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService executorService=Executors.newFixedThreadPool(2);//定义了线程池中最大存在的线程数目...
        
        //添加了第一个任务...这个任务会一直被执行...
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        
        //添加第二个任务,被执行三次停止...
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int i=0;
                while(true){
                    i++;
                    System.out.println("bb");
                    if(i==3){
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }    
                }
            }
        });
        
        /*
         * @param
         * 第三个任务...只有当第二个任务被执行三次之后才能被执行...
         * 由于三次前,线程池已经满了,这个任务是轮不到被执行的..只能排队进行等待. 
         * 三次之后,第二个任务被终止,也就是线程池中出现了空闲的状态,所以这个任务将被放入到线程池中执行...
         * */
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("cc");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}


ii.executorService.submit(Runnable) 第二种调用方式...这种方式与第一种的区别在于可以使用一个Future对象来判断当前的线程是否执行完毕...但是这种方法只能判断当前的线程是否执行完毕,无法返回数据信息...

Future future = executorService.submit(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});
//如果任务结束执行则返回 null
System.out.println("future.get()=" + future.get());



  iii.executorService.submit(Callable)...  第三种调用方式...这种调用方式与前一种有所不同,传递的参数为Callable对象,Callable与Runnbale很相似,但是Callable的call()方法可以返回数据信息...通过Future就能够获取到其中的信息..而Runnbale.run()方法时无法获取数据信息的....Future应用于多线程...可以获取call()方法返回的数据信息...其实他是一种模式,是为了性能优化而提供的一种思想...这里我就不说Future...


uture future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});
System.out.println("future.get() = " + future.get());
//上述样例代码会输出如下结果: 
//Asynchronous Callable
//future.get() = Callable Result



iv.inVokeAny()...第四种调用方式...方法 invokeAny() 接收一个包含 Callable 对象的集合作为参数。调用该方法不会返回 Future 对象,而是返回集合中某一个 Callable 对象的结果,而且无法保证调用之后返回的结果是哪一个Callable,只知道它是这些 Callable 中一个执行结束的 Callable 对象...说实话这个方法我不知道它创建的目的到底是什么...这里执行后的结果是随机的...也就是输出是不固定的....

ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});
String result = executorService.invokeAny(callables);
System.out.println("result = " + result);



v.inVokeAll()这个方法和上面不同的地方就在于它可以返回所有Callable的执行结果...获取到所有的执行结果,我们可以对其进行管理...相对而言,我觉得这个方法比上一个更实用吧...

ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 1";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 2";
    }
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
        return "Task 3";
    }
});
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future : futures){
    System.out.println("future.get = " + future.get());



3.线程池的关闭...

当我们不需要使用线程池的时候,我们需要对其进行关闭...有两种方法可以关闭掉线程池...

i.shutdown()...

  shutdown并不是直接关闭线程池,而是不再接受新的任务...如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池....

ii.shutdownNow()

  这个方法表示不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止...

大家自己试着运行下面的代码就了解其中到底是怎么回事了...

package executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Executor {
    /**
     * @param args
     * 
     */
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService executorService=Executors.newFixedThreadPool(1);//定义了线程池中最大存在的线程数目...
        
        //添加了第一个任务...这个执行三次停止...
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int j=0;
                while(true){
                    j++;
                    System.out.println("aa");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    if(j==3){
                        break;
                    }
                }
            }
        });
        
        //添加第二个任务,由于使用executorService.shutdown(),由于它的加入是在这个方法调用之前的,因此这个任务也会被执行...
        //如果我们使用了executorService.shutdownNow();方法,就算是他在之前加入的,由于调用了executorService.shutdownNow()方法
        //那么这个任务将直接被移出队列并且不会被执行...
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                int i=0;
                while(true){
                    i++;
                    System.out.println("bb");
                    if(i==3){
                        break;
                    }
                }
            }
        });
        executorService.shutdown();//这里无论使用了那种方法,都会抛出一个异常...
        /*
         * @param
         * 第三个任务...只有当第二个任务被执行三次之后才能被执行...
         * 由于三次前,线程池已经满了,这个任务是轮不到被执行的..只能排队进行等待. 
         * 三次之后,第二个任务被终止,也就是线程池中出现了空闲的状态,所以这个任务将被放入到线程池中执行...
         * */
        executorService.execute(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true){
                    
                    System.out.println("cc");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}



ExecutorService线程池的用法


 在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动、调度、管理线程的一大堆API了。在Java5以后,通过 Executor来启动线程比用Thread的start()更好。在新特征中,可以很容易控制线程的启动、执行和关闭过程,还可以很容易使用线程池的特性。


一、创建任务


任务就是一个实现了Runnable接口的类。
创建的时候实run方法即可。


二、执行任务


通过java.util.concurrent.ExecutorService接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其停止接受新任务。关闭后,执行程序将最后终止,这时没有任务在执行,也没有任务在等待执行,并且无法提交新任务。
executorService.execute(new TestRunnable());


1、创建ExecutorService


通过工具类java.util.concurrent.Executors的静态方法来创建。
Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
比如,创建一个ExecutorService的实例,ExecutorService实际上是一个线程池的管理工具:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();


2、将任务添加到线程去执行


当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。


三、关闭执行服务对象


executorService.shutdown();


五、获取任务的执行的返回值


在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被 ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的(<T> task) 方法来执行,并且返回一个 <T><T>,是表示任务等待完成的 Future。
public interface Callable<V>
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
类包含一些从其他普通形式转换成 Callable 类的实用方法。
Callable中的call()方法类似Runnable的run()方法,就是前者有返回值,后者没有。
当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。
同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。
遗憾的是,在Java API文档中,这块介绍的很糊涂,估计是翻译人员还没搞清楚的缘故吧。或者说是注释不到位。下面看个例子:


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
publicclass CallableDemo {
publicstaticvoid main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>();
//创建10个任务并执行
for (int i = 0; i < 10; i++) {
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = executorService.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
resultList.add(future);
}
//遍历任务的结果
for (Future<String> fs : resultList) {
try {
System.out.println(fs.get()); //打印各个线程(任务)执行的结果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。
executorService.shutdown();
}
}
}
}
class TaskWithResult implements Callable<String> {
privateint id;
public TaskWithResult(int id) {
this.id = id;
}
public String call() throws Exception {
System.out.println("call()方法被自动调用,干活!!! " + Thread.currentThread().getName());
//一个模拟耗时的操作
for (int i = 999999; i > 0; i--) ;
return"call()方法被自动调用,任务的结果是:" + id + " " + Thread.currentThread().getName();
}
}


运行结果:


call()方法被自动调用,干活!!! pool-1-thread-1
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-5
call()方法被自动调用,任务的结果是:0 pool-1-thread-1
call()方法被自动调用,任务的结果是:1 pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-2
call()方法被自动调用,干活!!! pool-1-thread-6
call()方法被自动调用,干活!!! pool-1-thread-4
call()方法被自动调用,任务的结果是:2 pool-1-thread-3
call()方法被自动调用,干活!!! pool-1-thread-3
call()方法被自动调用,任务的结果是:3 pool-1-thread-4
call()方法被自动调用,任务的结果是:4 pool-1-thread-5
call()方法被自动调用,任务的结果是:5 pool-1-thread-6
call()方法被自动调用,任务的结果是:6 pool-1-thread-2
call()方法被自动调用,任务的结果是:7 pool-1-thread-6
call()方法被自动调用,任务的结果是:8 pool-1-thread-4
call()方法被自动调用,任务的结果是:9 pool-1-thread-3

几种不同的ExecutorService线程池对象


1.newCachedThreadPool()      -缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse.如果没有,就建一个新的线程加入池中
-缓存型池子通常用于执行一些生存期很短的异步型任务
 因此在一些面向连接的daemon型SERVER中用得不多。
-能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
  注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。
2. newFixedThreadPool     -newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
-其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
-和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
-从方法的源代码看,cache池和fixed 池调用的是同一个底层池,只不过参数不同:
fixed池线程数固定,并且是0秒IDLE(无IDLE)
cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE  
3.ScheduledThreadPool     -调度型线程池
-这个池子里的线程可以按schedule依次delay执行,或周期执行
4.SingleThreadExecutor     -单例线程,任意时间池中只能有一个线程
-用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)

上面四种线程池,都使用Executor的缺省线程工厂建立线程,也可单独定义自己的线程工厂
下面是缺省线程工厂代码:

    static class DefaultThreadFactory implements ThreadFactory {
        static final AtomicInteger poolNumber = new AtomicInteger(1);
        final ThreadGroup group;
        final AtomicInteger threadNumber = new AtomicInteger(1);
        final String namePrefix;
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null)? s.getThreadGroup() :Thread.currentThread().getThreadGroup();
          
            namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
        }
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }


也可自己定义ThreadFactory,加入建立池的参数中


 public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {


Executor的execute()方法


execute() 方法将Runnable实例加入pool中,并进行一些pool size计算和优先级处理
execute() 方法本身在Executor接口中定义,有多个实现类都定义了不同的execute()方法
如ThreadPoolExecutor类(cache,fiexed,single三种池子都是调用它)的execute方法如下:


    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }


[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • C#创建自定义控件及添加自定义属性和事件使用实例详解

    这篇文章主要给大家介绍了关于C#创建自定义控件及添加自定义属性和事件使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-06-25
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • 自定义jquery模态窗口插件无法在顶层窗口显示问题

    自定义一个jquery模态窗口插件,将它集成到现有平台框架中时,它只能在mainFrame窗口中显示,无法在顶层窗口显示. 解决这个问题的办法: 通过以下代码就可能实现在顶层窗口弹窗 复制代码 代码如下: $(window.top.documen...2014-05-31
  • 自定义feignClient的常见坑及解决

    这篇文章主要介绍了自定义feignClient的常见坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • pytorch 自定义卷积核进行卷积操作方式

    今天小编就为大家分享一篇pytorch 自定义卷积核进行卷积操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-06
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • PHP YII框架开发小技巧之模型(models)中rules自定义验证规则

    YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证...2015-11-24
  • jquery自定义插件开发之window的实现过程

    这篇文章主要介绍了jquery自定义插件开发之window的实现过程的相关资料,需要的朋友可以参考下...2016-05-09
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02