AndroidProjects个人项目归纳

AndroidProjects

1.Data Binding框架MVVM

项目源码位置:AndroidProjects/DataBinding目录

介绍

  • Data binding 在2015年7月发布的Android Studio v1.3.0 版本上引入,在2016年4月Android Studio v2.0.0 上正式支持。目前为止,Data Binding 已经支持双向绑定了。

  • Data Binding 解决了 Android UI 编程中的一个痛点,官方原生支持 MVVM 模型可以让我们在不改变既有代码框架的前提下,非常容易地使用这些新特性;

  • Databinding 是一个实现数据和UI绑定的框架,是一个实现 MVVM 模式的工具,有了 Data Binding,在Android中也可以很方便的实现MVVM开发模式。

  • Data Binding 是一个support库,最低支持到Android 2.1(API Level 7+)。

  • Data Binding 之前,我们不可避免地要编写大量的毫无营养的代码,如 findViewById()、setText(),setVisibility(),setEnabled() 或 setOnClickListener() 等,通过 Data Binding , 我们可以通过声明式布局以精简的代码来绑定应用程序逻辑和布局,这样就不用编写大量的毫无营养的代码了。

参考资料

Gradle配置:

android {  
    dataBinding {
        enabled = true
    }
}

效果图

DataBinding-art

示例代码

/**
 * BaseActivity
 *
 * @author Edwin.Wu
 * @version 2016/11/25 16:21
 * @since JDK1.8
 */
public abstract class BaseActivity<T extends ViewDataBinding> extends AppCompatActivity {
    protected Context mContext;
    protected T b;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        bindView();
        setUpData();
    }

    @LayoutRes
    protected abstract int getLayoutResId();

    protected void bindView() {
        b = DataBindingUtil.setContentView(this, getLayoutResId());
    }

    protected abstract void setUpData();

}
/**
 * BaseFragment
 *
 * @author Edwin.Wu
 * @version 2016/11/25 16:20
 * @since JDK1.8
 */
public abstract class BaseFragment<T extends ViewDataBinding> extends Fragment {
    protected Context mContext;
    protected T b;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = getActivity();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        bindView(inflater, container);
        setUpData();
        return b.getRoot();
    }


    @LayoutRes
    protected abstract int getLayoutResId();

    private void bindView(LayoutInflater inflater, ViewGroup container) {
        b = DataBindingUtil.inflate(inflater, getLayoutResId(), container, false);
    }

    protected abstract void setUpData();
}
/**
 * BindingViewHolder
 *
 * @author Edwin.Wu
 * @version 2016/11/28 11:01
 * @since JDK1.8
 */
class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
    private T binding;

    BindingViewHolder(View itemView) {
        super(itemView);
    }

    public T getBinding() {
        return binding;
    }

    public void setBinding(T binding) {
        this.binding = binding;
    }
}
/**
 * ListView
 *
 * @author Edwin.Wu
 * @version 2016/11/28 00:25
 * @since JDK1.8
 */
public class ListViewAdapter extends BaseAdapter {
    private List<Bean.DataBean> data;
    private LayoutInflater mLayoutInflater;
    private ItemListBinding binding;

    public ListViewAdapter(Context context, List<Bean.DataBean> allData) {
        this.data = allData;
        this.mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Bean.DataBean getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_list, parent, false);
            convertView = binding.getRoot();
            convertView.setTag(binding);
        } else {
            binding = (ItemListBinding) convertView.getTag();
        }
        binding.setData(data.get(position));
        return convertView;
    }
}
/**
 * RecyclerView
 *
 * @author Edwin.Wu
 * @version 2016/11/28 11:02
 * @since JDK1.8
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter<BindingViewHolder<ItemRecyclerViewBinding>> {
    private List<Bean.DataBean> data;
    private LayoutInflater mLayoutInflater;

    private onItemClickLister mLister;

    public interface onItemClickLister {
        void onItemClick(View itemView, Bean.DataBean employee, int position);

    }

    public RecyclerViewAdapter(Context context, List<Bean.DataBean> data) {
        this.mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.data = data;
    }

    @Override
    public BindingViewHolder<ItemRecyclerViewBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
        ItemRecyclerViewBinding binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.item_recycler_view, parent, false);

        BindingViewHolder<ItemRecyclerViewBinding> holder = new BindingViewHolder<ItemRecyclerViewBinding>(binding.getRoot());

        holder.setBinding(binding);

        return holder;
    }

    @Override
    public void onBindViewHolder(final BindingViewHolder<ItemRecyclerViewBinding> holder, final int position) {
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mLister != null) {
                    mLister.onItemClick(holder.itemView, data.get(position), position);
                }
            }
        });
        holder.getBinding().setData(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }


    public void setOnItemClickLister(onItemClickLister mLister) {
        if (mLister != null) {
            this.mLister = mLister;
        } else {
            throw new NullPointerException("onItemClickLister is null");
        }
    }


    public void addAll(List<Bean.DataBean> beanList) {
        if (beanList != null) {
            data.addAll(beanList);
        } else {
            throw new NullPointerException("List<Bean.DataBean> is null");
        }
    }


    public void add(int position, Bean.DataBean dataBean) {
        if (dataBean != null) {
            data.add(position, dataBean);
            notifyItemInserted(position);
        } else {
            throw new NullPointerException("dataBean is null");
        }
    }

    public void remove(int position) {
        if (data.size() == 0) {
            return;
        }
        data.remove(position);
        notifyItemRemoved(position);
    }
}
/**
 * ViewModel
 * <p>
 * dataBinding绑定
 *
 * @author Edwin.Wu
 * @version 2016/11/25 20:38
 * @since JDK1.8
 */
public class ViewBindingAdapter {

    /**
     * dataBinding
     * 图片加载
     *
     * @param view
     * @param url
     * @param drawable
     */
    @BindingAdapter({"app:imageUrl", "app:placeHolder"})
    public static void loadImageFromUrl(ImageView view, String url, Drawable drawable) {
        Glide.with(view.getContext())
                .load(url)
                .placeholder(drawable)
                .diskCacheStrategy(DiskCacheStrategy.RESULT)
                .error(drawable)
                .into(view);
    }
item_recycler_view.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="data"
            type="com.github.why168.databinding.model.Bean.DataBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/image_recycler_view"
            android:layout_width="100dp"
            android:layout_height="100dp"
            app:imageUrl="@{data.img}"
            app:placeHolder="@{@drawable/ic_launcher}" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_recycler_title"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="@{data.title}"
                android:textSize="15sp" />

            <TextView
                android:id="@+id/tv_recycler_subtitle"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="@{data.subtitle}"
                android:textSize="18sp" />
        </LinearLayout>

    </LinearLayout>
</layout>

2. BaseView

项目源码位置:AndroidProjects/BaseView目录

介绍

  • 手写代码通过参数配置布局,提高APP性能,提高复用。

参考资料

效果图

DataBinding-art

示例代码

/**
 * MainActivity
 *
 * @author Edwin.Wu
 * @version 2016/5/28 21:15
 * @since JDK1.8
 */
public class MainActivity extends AppCompatActivity implements OnRowClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ContainerView mWidgetRowContainerView = (ContainerView) findViewById(R.id.mWidgetRowContainerView);
        ArrayList<GroupDescriptor> groupDescriptors = new ArrayList<GroupDescriptor>();

        ArrayList<BaseRowDescriptor> descriptors1 = new ArrayList<BaseRowDescriptor>();
        descriptors1.add(new RowProfileDescriptor("imagurl", "Edwin", "WeChat ID:wuhaoyou_949", RowActionEnum.MY_POSTS));
        GroupDescriptor groupDescriptor1 = new GroupDescriptor(descriptors1);
        groupDescriptors.add(groupDescriptor1);

        ArrayList<BaseRowDescriptor> descriptors2 = new ArrayList<BaseRowDescriptor>();
        descriptors2.add(new RowDescriptor(R.mipmap.more_my_album, "My Posts", RowActionEnum.ALBUM));
        descriptors2.add(new RowDescriptor(R.mipmap.more_my_favorite, "Favorites", RowActionEnum.FAVORITE));
        descriptors2.add(new RowDescriptor(R.mipmap.more_my_bank_card, "Wallet", RowActionEnum.BANK_CARD));
        descriptors2.add(new RowDescriptor(R.mipmap.emotionstore_custom_icon, "Cards & Passes", RowActionEnum.CUSTOM_ICON));
        GroupDescriptor groupDescriptor2 = new GroupDescriptor(descriptors2);
        groupDescriptors.add(groupDescriptor2);

        ArrayList<BaseRowDescriptor> descriptors3 = new ArrayList<BaseRowDescriptor>();
        descriptors3.add(new RowDescriptor(R.mipmap.emotionstore_emoji_icon, "Sticker Gallery", RowActionEnum.EMOJI_ICON));
        GroupDescriptor groupDescriptor3 = new GroupDescriptor(descriptors3);
        groupDescriptors.add(groupDescriptor3);

        ArrayList<BaseRowDescriptor> descriptors4 = new ArrayList<BaseRowDescriptor>();
        descriptors4.add(new RowDescriptor(R.mipmap.more_setting, "Settings", RowActionEnum.SETTING));
        GroupDescriptor groupDescriptor4 = new GroupDescriptor(descriptors4);
        groupDescriptors.add(groupDescriptor4);


        mWidgetRowContainerView.initializeData(groupDescriptors, this);
        mWidgetRowContainerView.notifyDataChanged();
    }

    @Override
    public void onRowClick(RowActionEnum actionEnum) {
        Toast.makeText(this, "RowActionEnum--->" + actionEnum.name(), Toast.LENGTH_SHORT).show();

        //TODO 根据不同的action实现逻辑
        switch (actionEnum) {
            case ALBUM:
                break;
            case FAVORITE:
                break;
            case BANK_CARD:
                break;
            case CUSTOM_ICON:
                break;
            case EMOJI_ICON:
                break;
            case SETTING:
                break;
        }
    }
}

3.CollapseView

项目源码位置:AndroidProjects/CollapseView目录

介绍

  • CollapseView 折叠/展开
  • FlowLayout 流式布局

参考资料

效果图

DataBinding-art

示例代码

/**
 * 折叠/展开ViewGroup
 *
 * @author Edwin.Wu
 * @version 2016/06/05 上午1:57
 * @since JDK1.8
 */
public class CollapseView extends LinearLayout {
    private final Context mContext;
    private long duration = 555;//展开/折叠的时间(s)
    private TextView mNumberTextView;
    private TextView mTitleTextView;
    private RelativeLayout mTitleRelativeLayout;
    private RelativeLayout mContentRelativeLayout;
    private ImageView mArrowImageView;
    private int parentWidthMeasureSpec;
    private int parentHeightMeasureSpec;

    public CollapseView(Context context) {
        super(context);
        mContext = context;
        LayoutInflater.from(context).inflate(R.layout.collapse_layout, this);
        init();
    }

    public CollapseView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        LayoutInflater.from(context).inflate(R.layout.collapse_layout, this);
        init();
    }

    public CollapseView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        LayoutInflater.from(context).inflate(R.layout.collapse_layout, this);
        init();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        parentWidthMeasureSpec = widthMeasureSpec;
        parentHeightMeasureSpec = heightMeasureSpec;
    }

    private void init() {
        mNumberTextView = (TextView) findViewById(R.id.numberTextView);
        mTitleTextView = (TextView) findViewById(R.id.titleTextView);
        mTitleRelativeLayout = (RelativeLayout) findViewById(R.id.titleRelativeLayout);
        mContentRelativeLayout = (RelativeLayout) findViewById(R.id.contentRelativeLayout);
        mArrowImageView = (ImageView) findViewById(R.id.arrowImageView);
        mTitleRelativeLayout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                rotateArrow();
            }
        });

        collapse(mContentRelativeLayout);
    }

    public void setNumber(String number) {
        if (!TextUtils.isEmpty(number)) {
            mNumberTextView.setText(number);
        }
    }

    public void setTitle(String title) {
        if (!TextUtils.isEmpty(title)) {
            mTitleTextView.setText(title);
        }
    }

    public void setContent(int resID) {
        View view = LayoutInflater.from(mContext).inflate(resID, null);
        RelativeLayout.LayoutParams layoutParams =
                new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT);
        view.setLayoutParams(layoutParams);


        mContentRelativeLayout.addView(view);
    }

    private void rotateArrow() {
        int angle = 0;
        if (mArrowImageView.getTag() == null || mArrowImageView.getTag().equals(true)) {
            mArrowImageView.setTag(false);
            angle = -180;
            //TODO 展开
            expand(mContentRelativeLayout);
        } else {
            angle = 0;
            mArrowImageView.setTag(true);
            //TODO 折叠
            collapse(mContentRelativeLayout);
        }
        mArrowImageView.animate().setDuration(duration).rotation(angle);
    }

    /**
     * 折叠
     *
     * @param view 视图
     */
    private void collapse(final View view) {
        final int measuredHeight = view.getMeasuredHeight();
        Animation animation = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                Log.e("TAG", "interpolatedTime = " + interpolatedTime);
                if (interpolatedTime == 1) {
                    mContentRelativeLayout.setVisibility(GONE);
                } else {
                    view.getLayoutParams().height = measuredHeight - (int) (measuredHeight * interpolatedTime);
                    view.requestLayout();
                }
            }
        };
        animation.setDuration(duration);
        view.startAnimation(animation);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
    }

    /**
     * 展开
     *
     * @param view 视图
     */
    private void expand(final View view) {
        view.measure(parentWidthMeasureSpec, parentHeightMeasureSpec);
        final int measuredHeight = view.getMeasuredHeight();
        view.setVisibility(View.VISIBLE);

        Animation animation = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                Log.e("TAG", "interpolatedTime = " + interpolatedTime);
                if (interpolatedTime == 1) {
                    view.getLayoutParams().height = measuredHeight;
                } else {
                    view.getLayoutParams().height = (int) (measuredHeight * interpolatedTime);
                }
                view.requestLayout();
            }

            @Override
            public boolean willChangeBounds() {
                return true;
            }

        };
        animation.setDuration(duration);
        view.startAnimation(animation);
    }
}

4.Notification

项目源码位置:AndroidProjects/Notification目录

介绍

  • Notification API

参考资料

效果图

DataBinding-art

示例代码

PendingIntent activities = PendingIntent.getActivity(getApplicationContext()
                , 1
                , new Intent(getApplicationContext(), ResultActivity.class)
                , Intent.FILL_IN_ACTION);


        RemoteInput remoteInput = new RemoteInput.Builder("")
                .setLabel("label")
                .build();

        // Build an Android N compatible Remote Input enabled action.
        NotificationCompat.Action actionReplyByRemoteInput = new NotificationCompat.Action.Builder(
                R.mipmap.ic_launcher, "输入框", activities)
                .addRemoteInput(remoteInput)
                .build();

        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.item_remote_view);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setVisibility(View.VISIBLE)
                .setContentTitle("My notification")//标题
                .setContentText("Hello World!")//正文
                .setSubText("SubText")//设置在平台通知模板文本的第三行
                .setRemoteInputHistory(new CharSequence[]{"1", "2", "3"})//设置远程输入历史。
                .setContent(remoteViews)//提供一个定制RemoteViews,而不是使用标准之一。
                .setDeleteIntent(activities)//供应PendingIntent时通报用户直接从通知面板清除发送。
                .setFullScreenIntent(activities, false)//意图发动,而不是张贴通知状态栏。
                .setTicker("Tocker")//设置显示在状态栏时通报首先到达的文本。
                .setSortKey("排序键")//设置听命于同一包内的其他通知中此通知的排序键
                .setOnlyAlertOnce(false)//设置此标志,如果你只喜欢的声音,震动和股票要如果通知尚未显示播放。
                .setPriority(NotificationCompat.PRIORITY_MAX)//设置相对优先级此通知。
                .setColor(2554444)//颜色
                .setCategory(NotificationCompat.CATEGORY_PROMO)//设置的通知类别。
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.android_contact))//设置大图标所示股票和通知。
                .setNumber(3)//设置信息条数
                .setContentInfo("在右边设置大型文本的通知")
                .setWhen(System.currentTimeMillis())//设置时间
                .setShowWhen(true)//现实When
                .setContentIntent(activities)//设置Intent
                .setVibrate(new long[]{3000, 1000, 3000, 1000})//震动
                .setLights(0xff0000ff, 300, 0)//闪光灯
                .setAutoCancel(true)//点击之后自动消失
                .setOngoing(false)//用户不能手动清除
                .setProgress(100, 50, false)//进度条
                .addAction(actionReplyByRemoteInput);


        if (isPlayMusic)
            mBuilder.setSound(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.actor));//设置声音播放。它将在默认流上播放。
        else
            mBuilder.setDefaults(Notification.DEFAULT_ALL);//设置将要使用的默认通知选项。

        NotificationManagerCompat.from(getApplicationContext()).notify(count, mBuilder.build());

5.MultiChannelBuild

项目源码位置:AndroidProjects/MultiChannelBuild目录

介绍

  • Gradle多渠道打包
  • Gradle动态打包(包名,Logo,APP名称)
  • Gradle对生成APK名称重命名
  • Gradle签名配置
  • Gradle支持lambda
  • Gralde编译JDK1.8
  • Gradle存储库maven仓库设置

Gradle常见命令

  • ./gradlew clean:清理删除build文件夹
  • ./gradlew build:检查依赖并编译打包debug和release版本
  • ./gradlew -v:版本号
  • ./gradlew assembleRelease:打包所有渠道release版本
  • ./gradlew assembleBaiduRelease:打某个渠道的release版本
  • ./gradlew assembleDebug:打包所有渠道debug版本
  • ./gradlew assembleBaiduDebug:打某个渠道的debug版本
  • ./gradlew installRelease:打包并安装Release模式包
    • ./gradlew installBaiduRelease:同上
  • ./gradlew uninstallRelease:卸载Release模式包
    • ./gradlew uninstallBaiduRelease:同上
Gradle注意事项
  • linux下使用./gradlew
  • windows下使用gradlew
  • 打包后的apk文件在app–>build–>outputs—>apk
  • 使用gradlew时可能出现没有找到该命令,需要chmod 755 gradlew

示例代码

apply plugin: 'com.android.application'

android {
    /**
     * apk签名脚本
     */
    signingConfigs {
        config {
            keyAlias 'Edwin'
            keyPassword 'Edwin666666'
            storeFile file('./AppKeyStore.jks')
            storePassword '666666'
            v2SigningEnabled false //Android 7.0 中新增了 APK Signature Scheme v2 签名方式
        }
    }
    /**
     * 自定义包名
     */
    def pageName = "com.github.why168.multichannelbuild"
    /**
     * 自定义名字
     */
    def appName = "MultiChannelBuild"
    /**
     * 自定义Logo
     */
    def appLogo = "@mipmap/ic_launcher"

    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId pageName
        minSdkVersion 11
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"

        /**
         * 支持lambda
         */
        jackOptions {
            enabled true
        }

        /**
         * 类似下面productFlavors
         */
        manifestPlaceholders = [
                APP_ID  : "APP_ID111111",
                APP_KEY : "APP_KEY222222",
                APP_NAME: appName,
                APP_LOGO: appLogo
        ]

        /**
         * Native Development Kit
         */
        ndk {
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'mips'
        }
    }
    buildTypes {
        debug {
            debuggable true
            buildConfigField "boolean", "LOG_DEBUG", "true"
            minifyEnabled false   //混淆
            shrinkResources false //去除无效的资源文件
            zipAlignEnabled false //Zipalign优化
            signingConfig signingConfigs.config
        }
        release {
            debuggable false
            buildConfigField "boolean", "LOG_DEBUG", "false"
            minifyEnabled true
            shrinkResources false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
        }
    }
    /**
     * 源设置
     */
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    /**
     * 多渠道配置,下面的关键字'CHANNEL_NAME'与AndroidManifest里面必须一致。
     */
    productFlavors {
        baidu {//百度市场
            manifestPlaceholders = [CHANNEL_NAME: 500001]
        }
        yingyongbai {//腾讯应用市场
            manifestPlaceholders = [CHANNEL_NAME: 500002];
        }
        xiaomi {//小米应用市场
            manifestPlaceholders = [CHANNEL_NAME: 500003];
        }
        store360 {//360商店
            manifestPlaceholders = [CHANNEL_NAME: 500004];
        }
        anzhi {//安智市场
            manifestPlaceholders = [CHANNEL_NAME: 500005];
        }
    }
    /**
     * apk名字自定义
     */
    android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            def releaseTime = new Date().format("yyyy-MM-dd HH:MM", TimeZone.getTimeZone("UTC"))
            def fileName = outputFile.name.replace(".apk", "-V" + defaultConfig.versionName + "_" + releaseTime + ".apk")
            output.outputFile = new File(outputFile.parent, fileName)

        }
    }
    /**
     * JDK1.8编译选项
     */
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    /**
     * 存储库maven仓库设置
     */
    repositories {
        flatDir {
            dirs 'libs'
        }
        mavenCentral()
        jcenter()
        maven { url "https://jitpack.io" }
    }
    /**
     * 移除lint检测的error
     */
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }

    aaptOptions {
        cruncherEnabled = false
        useNewCruncher = false
    }
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:25.1.0'
}

6.Activity滑动关闭

项目源码位置:AndroidProjects/SwipeBack目录

介绍

  • 通过修改 support-v4 包中 SlidingPaneLayout 的源码来实现滑动返回布局
  • 动态设置滑动返回是否可用
  • 动态设置是否仅仅跟踪左侧边缘的滑动返回
  • 动态设置是否是微信滑动返回样式
  • 动态设置是否显示滑动返回的阴影效果

参考资料

  • https://github.com/bingoogolapple/BGASwipeBackLayout-Android

效果图

DataBinding-art

示例代码

/**
 * 滑动返回Activity基类
 *
 * @author Edwin.Wu
 * @version 2017/2/7 18:05
 * @since JDK1.8
 */
public class SwipeBackActivity extends AppCompatActivity implements SwipeBackHelper.Delegate {
    protected SwipeBackHelper mSwipeBackHelper;

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

    private void initSwipeBack() {
        mSwipeBackHelper = new SwipeBackHelper(this, this);
        // 设置滑动返回是否可用。默认值为 true
        mSwipeBackHelper.setSwipeBackEnable(true);
        // 设置是否仅仅跟踪左侧边缘的滑动返回。默认值为 true
        mSwipeBackHelper.setIsOnlyTrackingLeftEdge(true);
        // 设置是否是微信滑动返回样式。默认值为 true
        mSwipeBackHelper.setIsWeChatStyle(true);
        // 设置阴影资源 id。默认值为 R.drawable.bga_sbl_shadow
        mSwipeBackHelper.setShadowResId(R.drawable.bga_sbl_shadow);
        // 设置是否显示滑动返回的阴影效果。默认值为 true
        mSwipeBackHelper.setIsNeedShowShadow(true);
        // 设置阴影区域的透明度是否根据滑动的距离渐变。默认值为 true
        mSwipeBackHelper.setIsShadowAlphaGradient(true);

    }

    @Override
    public boolean isSupportSwipeBack() {
        return true;
    }

    /**
     * 正在滑动返回
     *
     * @param slideOffset 从 0 到 1
     */
    @Override
    public void onSwipeBackLayoutSlide(float slideOffset) {

    }

    /**
     * 没达到滑动返回的阈值,取消滑动返回动作,回到默认状态
     */
    @Override
    public void onSwipeBackLayoutCancel() {

    }

    /**
     * 滑动返回执行完毕,销毁当前 Activity
     */
    @Override
    public void onSwipeBackLayoutExecuted() {
        mSwipeBackHelper.swipeBackward();
    }

    /**
     * 返回操作
     */
    @Override
    public void onBackPressed() {
        mSwipeBackHelper.backward();
    }

    /**
     * 设置状态栏颜色
     *
     * @param color number
     */
    protected void setStatusBarColor(@ColorInt int color) {
        setStatusBarColor(color, 0);
    }

    /**
     * 设置状态栏颜色
     *
     * @param color          number
     * @param statusBarAlpha 透明度
     */
    public void setStatusBarColor(@ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {
        StatusBarUtil.setColorForSwipeBack(this, color, statusBarAlpha);
    }
}

7.CustomTabs

项目源码位置:AndroidProjects/CustomTabs目录

介绍

Chrome 浏览器现在已经成为 Android 原生系统的默认浏览器了。如果在您的应用中需要打开网页内容,之前的做法要么使用 WebView 或者 直接打开第三方浏览器来显示内容。典型的场景比如微信里面的大部分内容都是先在微信自己的 WebView 中显示,然后你可以选择菜单中的在浏览器中打开。而 Chrome 团队认为现在在应用中显示网页内容已经非常常见了,为了方便大家显示网页内容并且保存良好的用户体验,实现了这么一个功能。具体来说,如果您想在应用中打开一个网页,你可以通过 Chrome Custom Tabs 来打开 Chrome 浏览器的一个自定义 Tab 来显示该网页,你可以自定义这个 Tab 的一些属性来保持良好的用户体验,并且让用户感觉这个自定义 Tab 就是您应用的一部分。目前可以自定义如下内容:
  • ActionBar(也就是最上面的 Toolbar,网址一栏)的颜色
  • 自定义 Tab 的进入和退出过场动画
  • 在自定义 Tab 的 ActionBar 上添加自定义图标和菜单
  • 自定义返回图标
  • 自定义 Tab 可以通过回调接口来通知应用网页导航的情况
  • 性能更好,使用 Custom Tab 来打开网页的时候,还可以预先加载网页内容,这样当打开的时候,用户感觉非常快。
  • 生命周期管理,使用 Custom tab 可以和您的应用绑定一起,当用户在浏览网页的时候,您的应用也被认为是互动的程序,不会被系统杀死。
  • 可以共享 Chrome 浏览器的 Cookie ,这样用户就不用再登录一遍网站了。
  • 如果用户开启了 Chrome 的数据压缩功能,则一样可以使用
  • 和 Chrome 一样的自动补全功能
  • 只需点击左上角的返回按钮一次就可以返回您的应用中了
  • 每次用的都是最新版本的 Chrome
何时选择使用 WebView 和 Chrome Custom Tabs 呢?
  • 如果你之前使用的不是 WebView ,则这种情况都应该用 Chrome Custom Tabs 来打开网页。如果你之前使用的是 WebView,则这里有两种情况来帮组您选择哪种情况更适合你:如果要显示的网页内容是由您自己控制的,并且网页内容需要和 Android 组件交互,比如通过 JavaScript 接口来调用 Android 系统的一些功能,这种情况下你还需要用 WebView 来实现;其他情况都可以用 Chrome Custom Tabs 来实现。

  • Chrome Custom Tabs 使用起来非常简单,简单的使用只需要一行代码,和直接调用系统浏览器显示网页没啥区别。通过简单的几项设定,就能让用户感觉浏览第三方网页就像您应用本身的功能一样。

使用注意
  • 在CustomTabActivityHelper这个类里面的openCustomTab这个方法说明了一下:packageName is null打开自己定义的WebviewActivity的WebView,is not null打开一个自定义选项卡URL
  • 我通过2个手机测试发现
    • 谷歌亲儿子Nexus5:打开的是Chrome浏览器
    • 乐视X608:打开都是都是自己定义的WebviewActivity的WebView
  • 总结国内的ROM厂商已经干掉了Chrome,所以使用这个库意义不大。

参考资料

效果图

DataBinding-art

示例代码

CustomTabActivityHelpe.java
/**
 * Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
 *
 * @param activity         The host activity.
 * @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
 * @param uri              the Uri to be opened.
 * @param fallback         a CustomTabFallback to be used if Custom Tabs is not available.
 */
public static void openCustomTab(Activity activity,
                                 CustomTabsIntent customTabsIntent,
                                 Uri uri,
                                 CustomTabFallback fallback) {
    String packageName = CustomTabsHelper.getPackageNameToUse(activity);

    //If we cant find a package name, it means theres no browser that supports
    //Chrome Custom Tabs installed. So, we fallback to the webview
    if (packageName == null) {
        if (fallback != null) {
            fallback.openUri(activity, uri);
        }
    } else {
        customTabsIntent.intent.setPackage(packageName);
        customTabsIntent.launchUrl(activity, uri);
    }
}
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();

// 修改 ActionBar 的颜色
intentBuilder.setToolbarColor(color);

// 设置辅助工具栏颜色
intentBuilder.setSecondaryToolbarColor(color);

// 添加一个分享按钮
String shareLabel = getString(R.string.label_action_share);
Bitmap icon = BitmapFactory.decodeResource(getResources(),android.R.drawable.ic_menu_share);
PendingIntent pendingIntent = createPendingIntent();
intentBuilder.setActionButton(icon, shareLabel, pendingIntent);

// 设置菜单
String menuItemTitle = getString(R.string.menu_item_title);
PendingIntent menuItemPendingIntent = createPendingIntent(ActionBroadcastReceiver.ACTION_MENU_ITEM);
intentBuilder.addMenuItem(menuItemTitle, menuItemPendingIntent);

// 设置默认
intentBuilder.addDefaultShareMenuItem();

// 是否显示网页标题
intentBuilder.setShowTitle(mShowTitleCheckBox.isChecked());

// 设置工具栏
String actionLabel = getString(R.string.label_action);
Bitmap icon = BitmapFactory.decodeResource(getResources(),android.R.drawable.ic_menu_share);
PendingIntent pendingIntent = createPendingIntent(ActionBroadcastReceiver.ACTION_TOOLBAR);
intentBuilder.addToolbarItem(TOOLBAR_ITEM_ID, icon, actionLabel, pendingIntent);
             
// 自定义关闭 Custom tabs 的图标
intentBuilder.setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back));

// 自定义 Activity 转场 动画
intentBuilder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left);

intentBuilder.setExitAnimations(this, android.R.anim.slide_in_left,android.R.anim.slide_out_right);

// 最后调用助手类 CustomTabActivityHelper 的 openCustomTab 函数来打开一个网址
CustomTabActivityHelper.openCustomTab(this, intentBuilder.build(), Uri.parse(url), new WebviewFallback());

8.更新中...

项目源码位置:AndroidProjects/xxx目录

介绍

参考资料

效果图

示例代码




















技术交流大本营

欢迎加入Android技术交流大群,群号码:554610222

Android技术交流,进群后请改名片.
例如:北京-李四.
群内交流以技术为主,乱发黄图乱发广告乱开车者一律踢.

Android技术交流大群

MIT License

Copyright (c) 2016 Edwin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

转载于:https://www.cnblogs.com/why168888/p/6142287.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值