美文网首页
Android建筑图像过滤器,如Instagram(1)

Android建筑图像过滤器,如Instagram(1)

作者: ListenToCode | 来源:发表于2018-10-10 15:59 被阅读75次

    Android建筑图像过滤器,如Instagram

    现在图像过滤器在很多Android应用程序中很常见。Instagram以其流行的过滤器功能而闻名,可能是第一款将图像过滤器引入Android世界的应用程序。还有很多其他图像编辑应用程序提供图像过滤器和图像编辑功能。

    在本文中,我们将学习如何构建像Instagram这样的图像过滤器应用程序。我们不会介绍精确的过滤器开发,但我们使用现有的图像过滤器库。

    下载代码下载.APK GITHUB

    1.如何构建图像滤镜

    通常,图像处理操作将以本机C / C ++语言完成。在android中,您可以使用C或C ++编写库,并使用JNI(Java Native Interface)通过Java代码访问函数。您还可以考虑使用像openCV这样的流行图像处理库来创建自己的过滤器库。

    在编写本机模块时,配置JNI是一个单独的主题,我想在另一篇文章中介绍它。现在我们将考虑使用本文中的现有图像过滤器库。

    2.使用图书馆(Zomato,Androidhive)

    在这篇文章中,我认为使用图像滤波器库AndroidPhotoFilters通过开发Zomato。该库提供基本的图像操作,如控制亮度饱和度对比度和少量图像滤镜。将所有这些功能组合在一起,您可以创建您赢得的过滤器。我对库进行了一些即兴创作并将其托管在公共maven存储库中,以便您可以非常轻松地集成到您的项目中。

    还记得库是非常基本的,你不能用Instagram实现像Instagram这样的伟大过滤器。要像Instagram一样构建过滤器,许多基础工作必须在本机级别完成。但我们会尝试实现类似于Instagram的过滤器。

    要使用该库,请在项目的依赖项中添加info.androidhive:imagefilters:1.0.7

    dependencies {

        implementation fileTree(dir: 'libs', include: ['*.jar'])

        // ...

        implementation 'info.androidhive:imagefilters:1.0.7'

    }

    有关该库的更详细的文档可以在Github页面上找到。

    3.图像过滤包

    我写了几个图像过滤器并将它们包含在库中。您可以使用下面的代码段获取Filter Pack。您可以循环它们并渲染每个过滤器的缩略图版本。您也可以直接访问单个过滤器。

    // get the filter pack

    List filters = FilterPack.getFilterPack(getActivity());

    for(Filter filter : filters) {

            ThumbnailItem item = newThumbnailItem();

            item.image = thumbImage;

            item.filter = filter;

            item.filterName = filter.getName();

            ThumbnailsManager.addThumb(tI);

    }

    // Accessing single filter...

    Bitmap bitmap = your_bitmap_;

    Filter clarendon = FilterPack.getClarendon();

    // apply filter

    imagePreview.setImageBitmap(filter.processFilter(bitmap));

    下面是每个过滤器的预览及其名称。

    过滤包

    施特鲁克克拉伦登奥德曼

    火星上升四月

    亚马逊星光耳语

    青柠阿里汉Bluemess

    阿黛尔克鲁兹都会

    奥黛丽  

    4.构建Instagram界面

    这里的想法是像Instagram一样构建界面,底部有两个标签。一个选项卡用于应用不同的滤镜,另一个用于控制图像调整,如亮度,对比度和饱和度。

    要实现此布局,我们需要将ViewPagerTabLayout结合使用。要在可滚动列表中呈现缩略图图像,需要使用RecyclerView。我们还需要两个Fragment类,一个用于渲染水平缩略图以预览滤镜效果。另一个片段是显示图像控件。

    如果您观察下面的图像,MainActivity.java用于实现整个布局。ImageFiltersFragment.java用于渲染水平缩略图图像。EditImageFrament.java用于渲染图像调整控件。每当应用过滤器或更改图像控件时,这两个片段都将提供回调方法。在主要活动中,将在回调时采取适当的行动。

    现在让我们从Android Studio中的一个新项目开始吧。

    1。从File⇒NewProject在Android Studio中创建一个新项目,然后从模板中选择Basic Activity

    2。开放的build.gradle位于下的应用程序文件夹,并添加androidhive的ImageFilters依赖。我还添加了其他必要的依赖,如ButterKnifeDexterRecyclerView

    的build.gradle

    dependencies {

        // ...

        // image filters

        implementation 'info.androidhive:imagefilters:1.0.7'

        // butter knife

        compile 'com.jakewharton:butterknife:8.8.1'

        annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

        // dexter M permissions

        compile 'com.karumi:dexter:4.1.0'

        compile 'com.android.support:recyclerview-v7:26.1.0'

    }

    3。将以下资源添加到相应的strings.xmlcolors.xmldimens.xmlstyles.xml文件中。

    strings.xml中

        Image Filters

        Filters

        Settings

        FILTERS

        EDIT

        BRIGHTNESS

        CONTRAST

        SATURATION

        FILTERS

        EDIT

        sans-serif-medium

        Normal

        SAVE

        OPEN

    colors.xml

        #3F51B5

        #303F9F

        #009688

        #FF3990

        #8A8889

        #221F20

    dimens.xml

        16dp

        80dp

        100dp

        8dp

        10dp

        10dp

        16dp

        100dp

    styles.xml

            @color/colorPrimary

            @color/colorPrimaryDark

            @color/colorAccent

            false

            true

            false

            true

            true

            @color/color_option_menu

    4。打开AndroidManifest.xml并添加READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限。将AppTheme.NoActionBar.Fullscreen主题添加到主活动以使活动全屏显示。

    AndroidManifest.xml中

    http://schemas.android.com/apk/res/android"

        package="info.androidhive.imagefilters">

            android:allowBackup="true"

            android:icon="@mipmap/ic_launcher"

            android:label="@string/app_name"

            android:roundIcon="@mipmap/ic_launcher_round"

            android:supportsRtl="true"

            android:theme="@style/AppTheme">

                android:name=".MainActivity"

                android:label="@string/app_name"

                android:theme="@style/AppTheme.NoActionBar.Fullscreen">

    5。打开位于res⇒ 菜单文件夹下的menu_main.xml,然后修改菜单项,如下所示。此菜单提供工具栏中的OPENSAVE选项以打开和保存图像。

    menu_main.xml

    http://schemas.android.com/apk/res/android"

        xmlns:app="http://schemas.android.com/apk/res-auto"

        xmlns:tools="http://schemas.android.com/tools"

        tools:context="info.androidhive.imagefilters.MainActivity">

            android:id="@+id/action_open"

            android:orderInCategory="100"

            android:title="@string/action_open"

            app:showAsAction="always"/>

            android:id="@+id/action_save"

            android:orderInCategory="101"

            android:title="@string/action_save"

            app:showAsAction="always"/>

    6。创建一个名为utils的新包。在这里,我们将保留此应用程序所需的辅助类。

    7。在utils包下,创建一个名为BitmapUtils.java的类。该类将具有从库加载图像,压缩图像,将图像保存到图库的方法。

    BitmapUtils.java

    packageinfo.androidhive.imagefilters.utils;

    importandroid.content.ContentResolver;

    importandroid.content.ContentUris;

    importandroid.content.ContentValues;

    importandroid.content.Context;

    importandroid.content.res.AssetManager;

    importandroid.content.res.Resources;

    importandroid.database.Cursor;

    importandroid.graphics.Bitmap;

    importandroid.graphics.BitmapFactory;

    importandroid.graphics.Matrix;

    importandroid.net.Uri;

    importandroid.provider.MediaStore;

    importandroid.util.Log;

    importjava.io.FileNotFoundException;

    importjava.io.IOException;

    importjava.io.InputStream;

    importjava.io.OutputStream;

    /**

     * Created by ravi on 06/11/17.

     */

    publicclassBitmapUtils {

        privatestaticfinalString TAG = BitmapUtils.class.getSimpleName();

        /**

         * Getting bitmap from Assets folder

         *

         * @return

         */

        publicstaticBitmap getBitmapFromAssets(Context context, String fileName, intwidth, intheight) {

            AssetManager assetManager = context.getAssets();

            InputStream istr;

            Bitmap bitmap = null;

            try{

                finalBitmapFactory.Options options = newBitmapFactory.Options();

                options.inJustDecodeBounds = true;

                istr = assetManager.open(fileName);

                // Calculate inSampleSize

                options.inSampleSize = calculateInSampleSize(options, width, height);

                // Decode bitmap with inSampleSize set

                options.inJustDecodeBounds = false;

                returnBitmapFactory.decodeStream(istr, null, options);

            } catch(IOException e) {

                Log.e(TAG, "Exception: "+ e.getMessage());

            }

            returnnull;

        }

        /**

         * Getting bitmap from Gallery

         *

         * @return

         */

        publicstaticBitmap getBitmapFromGallery(Context context, Uri path, intwidth, intheight) {

            String[] filePathColumn = {MediaStore.Images.Media.DATA};

            Cursor cursor = context.getContentResolver().query(path, filePathColumn, null, null, null);

            cursor.moveToFirst();

            intcolumnIndex = cursor.getColumnIndex(filePathColumn[0]);

            String picturePath = cursor.getString(columnIndex);

            cursor.close();

            finalBitmapFactory.Options options = newBitmapFactory.Options();

            options.inJustDecodeBounds = true;

            BitmapFactory.decodeFile(picturePath, options);

            // Calculate inSampleSize

            options.inSampleSize = calculateInSampleSize(options, width, height);

            // Decode bitmap with inSampleSize set

            options.inJustDecodeBounds = false;

            returnBitmapFactory.decodeFile(picturePath, options);

        }

        privatestaticintcalculateInSampleSize(

                BitmapFactory.Options options, intreqWidth, intreqHeight) {

            // Raw height and width of image

            finalintheight = options.outHeight;

            finalintwidth = options.outWidth;

            intinSampleSize = 1;

            if(height > reqHeight || width > reqWidth) {

                finalinthalfHeight = height / 2;

                finalinthalfWidth = width / 2;

                // Calculate the largest inSampleSize value that is a power of 2 and keeps both

                // height and width larger than the requested height and width.

                while((halfHeight / inSampleSize) >= reqHeight

                        && (halfWidth / inSampleSize) >= reqWidth) {

                    inSampleSize *= 2;

                }

            }

            returninSampleSize;

        }

        publicstaticBitmap decodeSampledBitmapFromResource(Resources res, intresId,

                                                             intreqWidth, intreqHeight) {

            // First decode with inJustDecodeBounds=true to check dimensions

            finalBitmapFactory.Options options = newBitmapFactory.Options();

            options.inJustDecodeBounds = true;

            BitmapFactory.decodeResource(res, resId, options);

            // Calculate inSampleSize

            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

            // Decode bitmap with inSampleSize set

            options.inJustDecodeBounds = false;

            returnBitmapFactory.decodeResource(res, resId, options);

        }

        /**

         * Storing image to device gallery

         * @param cr

         * @param source

         * @param title

         * @param description

         * @return

         */

        publicstaticfinalString insertImage(ContentResolver cr,

                                               Bitmap source,

                                               String title,

                                               String description) {

            ContentValues values = newContentValues();

            values.put(MediaStore.Images.Media.TITLE, title);

            values.put(MediaStore.Images.Media.DISPLAY_NAME, title);

            values.put(MediaStore.Images.Media.DESCRIPTION, description);

            values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");

            // Add the date meta data to ensure the image is added at the front of the gallery

            values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());

            values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());

            Uri url = null;

            String stringUrl = null;    /* value to be returned */

            try{

                url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

                if(source != null) {

                    OutputStream imageOut = cr.openOutputStream(url);

                    try{

                        source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);

                    } finally{

                        imageOut.close();

                    }

                    longid = ContentUris.parseId(url);

                    // Wait until MINI_KIND thumbnail is generated.

                    Bitmap miniThumb = MediaStore.Images.Thumbnails.getThumbnail(cr, id, MediaStore.Images.Thumbnails.MINI_KIND, null);

                    // This is for backward compatibility.

                    storeThumbnail(cr, miniThumb, id, 50F, 50F, MediaStore.Images.Thumbnails.MICRO_KIND);

                } else{

                    cr.delete(url, null, null);

                    url = null;

                }

            } catch(Exception e) {

                if(url != null) {

                    cr.delete(url, null, null);

                    url = null;

                }

            }

            if(url != null) {

                stringUrl = url.toString();

            }

            returnstringUrl;

        }

        /**

         * A copy of the Android internals StoreThumbnail method, it used with the insertImage to

         * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct

         * meta data. The StoreThumbnail method is private so it must be duplicated here.

         *

         * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method)

         */

        privatestaticfinalBitmap storeThumbnail(

                ContentResolver cr,

                Bitmap source,

                longid,

                floatwidth,

                floatheight,

                intkind) {

            // create the matrix to scale it

            Matrix matrix = newMatrix();

            floatscaleX = width / source.getWidth();

            floatscaleY = height / source.getHeight();

            matrix.setScale(scaleX, scaleY);

            Bitmap thumb = Bitmap.createBitmap(source, 0, 0,

                    source.getWidth(),

                    source.getHeight(), matrix,

                    true

            );

            ContentValues values = newContentValues(4);

            values.put(MediaStore.Images.Thumbnails.KIND, kind);

            values.put(MediaStore.Images.Thumbnails.IMAGE_ID, (int) id);

            values.put(MediaStore.Images.Thumbnails.HEIGHT, thumb.getHeight());

            values.put(MediaStore.Images.Thumbnails.WIDTH, thumb.getWidth());

            Uri url = cr.insert(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, values);

            try{

                OutputStream thumbOut = cr.openOutputStream(url);

                thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);

                thumbOut.close();

                returnthumb;

            } catch(FileNotFoundException ex) {

                returnnull;

            } catch(IOException ex) {

                returnnull;

            }

        }

    }

    8。在utils包下,创建一个名为NonSwipeableViewPager.java的类。这是自定义ViewPager元素,用于禁用页面之间的滑动功能

    NonSwipeableViewPager.java

    packageinfo.androidhive.imagefilters.utils;

    importandroid.content.Context;

    importandroid.support.v4.view.ViewPager;

    importandroid.util.AttributeSet;

    importandroid.view.MotionEvent;

    importandroid.view.animation.DecelerateInterpolator;

    importandroid.widget.Scroller;

    importjava.lang.reflect.Field;

    /**

     * Created by ravi on 24/10/17.

     * Custom viewpager disabling the swipe

     *https://stackoverflow.com/questions/9650265/how-do-disable-paging-by-swiping-with-finger-in-viewpager-but-still-be-able-to-s

     */

    publicclassNonSwipeableViewPager extendsViewPager {

        publicNonSwipeableViewPager(Context context) {

            super(context);

            setMyScroller();

        }

        publicNonSwipeableViewPager(Context context, AttributeSet attrs) {

            super(context, attrs);

            setMyScroller();

        }

        @Override

        publicbooleanonInterceptTouchEvent(MotionEvent event) {

            // Never allow swiping to switch between pages

            returnfalse;

        }

        @Override

        publicbooleanonTouchEvent(MotionEvent event) {

            // Never allow swiping to switch between pages

            returnfalse;

        }

        //down one is added for smooth scrolling

        privatevoidsetMyScroller() {

            try{

                Class viewpager = ViewPager.class;

                Field scroller = viewpager.getDeclaredField("mScroller");

                scroller.setAccessible(true);

                scroller.set(this, newMyScroller(getContext()));

            } catch(Exception e) {

                e.printStackTrace();

            }

        }

        publicclassMyScroller extendsScroller {

            publicMyScroller(Context context) {

                super(context, newDecelerateInterpolator());

            }

            @Override

            publicvoidstartScroll(intstartX, intstartY, intdx, intdy, intduration) {

                super.startScroll(startX, startY, dx, dy, 350/*1 secs*/);

            }

        }

    }

    9。在utils下创建另一个名为SpacesItemDecoration.java的类。这个类是在RecyclerView缩略图图像周围添加填充。padding-right将添加到所有缩略图图像中,但不会添加到列表中的最后一项。

    SpacesItemDecoration.java

    packageinfo.androidhive.imagefilters.utils;

    importandroid.graphics.Rect;

    importandroid.support.v7.widget.RecyclerView;

    importandroid.view.View;

    /**

     * Created by ravi on 23/10/17.

     */

    publicclassSpacesItemDecoration extendsRecyclerView.ItemDecoration {

        privateintspace;

        publicSpacesItemDecoration(intspace) {

            this.space = space;

        }

        @Override

        publicvoidgetItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

            if(parent.getChildAdapterPosition(view) == state.getItemCount() - 1) {

                outRect.left = space;

                outRect.right = 0;

            }else{

                outRect.right = space;

                outRect.left = 0;

            }

        }

    }

    5.水平缩略图RecyclerView适配器

    现在我们准备好了所有必需的课程。在跳转到实际UI之前,让我们创建RecyclerView适配器类。

    10。在res⇒布局下创建一个名为thumbnail_list_item.xml的新布局文件。此布局包含TextViewImageView以显示过滤器名称和缩略图图像。

    thumbnail_list_item.xml

    http://schemas.android.com/apk/res/android"

        android:layout_width="wrap_content"

        android:layout_height="match_parent"

        android:orientation="vertical">

            android:id="@+id/filter_name"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_gravity="center_horizontal"

            android:layout_marginBottom="5dp"

            android:layout_marginTop="5dp"

            android:fontFamily="@string/roboto_medium"/>

            android:id="@+id/thumbnail"

            android:layout_width="@dimen/thumbnail_size"

            android:layout_height="@dimen/thumbnail_size"

            android:src="@mipmap/ic_launcher"/>

    11。创建一个名为ThumbnailsAdapter.java的类此类充当RecyclerView缩略图适配器,以在水平列表中显示已过滤的图像。

    ThumbnailsAdapter.java

    packageinfo.androidhive.imagefilters;

    importandroid.content.Context;

    importandroid.support.v4.content.ContextCompat;

    importandroid.support.v7.widget.RecyclerView;

    importandroid.view.LayoutInflater;

    importandroid.view.View;

    importandroid.view.ViewGroup;

    importandroid.widget.ImageView;

    importandroid.widget.TextView;

    importcom.zomato.photofilters.imageprocessors.Filter;

    importcom.zomato.photofilters.utils.ThumbnailItem;

    importjava.util.List;

    importbutterknife.BindView;

    importbutterknife.ButterKnife;

    /**

     * Created by ravi on 23/10/17.

     */

    publicclassThumbnailsAdapter extendsRecyclerView.Adapter {

        privateList thumbnailItemList;

        privateThumbnailsAdapterListener listener;

        privateContext mContext;

        privateintselectedIndex = 0;

        publicclassMyViewHolder extendsRecyclerView.ViewHolder {

            @BindView(R.id.thumbnail)

            ImageView thumbnail;

            @BindView(R.id.filter_name)

            TextView filterName;

            publicMyViewHolder(View view) {

                super(view);

                ButterKnife.bind(this, view);

            }

        }

        publicThumbnailsAdapter(Context context, List thumbnailItemList, ThumbnailsAdapterListener listener) {

            mContext = context;

            this.thumbnailItemList = thumbnailItemList;

            this.listener = listener;

        }

        @Override

        publicMyViewHolder onCreateViewHolder(ViewGroup parent, intviewType) {

            View itemView = LayoutInflater.from(parent.getContext())

                    .inflate(R.layout.thumbnail_list_item, parent, false);

            returnnewMyViewHolder(itemView);

        }

        @Override

        publicvoidonBindViewHolder(MyViewHolder holder, finalintposition) {

            finalThumbnailItem thumbnailItem = thumbnailItemList.get(position);

            holder.thumbnail.setImageBitmap(thumbnailItem.image);

            holder.thumbnail.setOnClickListener(newView.OnClickListener() {

                @Override

                publicvoidonClick(View view) {

                    listener.onFilterSelected(thumbnailItem.filter);

                    selectedIndex = position;

                    notifyDataSetChanged();

                }

            });

            holder.filterName.setText(thumbnailItem.filterName);

            if(selectedIndex == position) {

                holder.filterName.setTextColor(ContextCompat.getColor(mContext, R.color.filter_label_selected));

            } else{

                holder.filterName.setTextColor(ContextCompat.getColor(mContext, R.color.filter_label_normal));

            }

        }

        @Override

        publicintgetItemCount() {

            returnthumbnailItemList.size();

        }

        publicinterfaceThumbnailsAdapterListener {

            voidonFilterSelected(Filter filter);

        }

    }

    6.添加图像过滤器列表片段

    现在我们将创建一个Fragment类,以在水平列表中呈现过滤后的图像缩略图。为此,我们需要一个RecyclerView并提供缩略图列表到适配器类。

    12。通过转到文件⇒新⇒片段⇒片段(空白)创建一个新片段,并将其命名为FiltersListFragment.java。这将创建一个包含必要布局文件的空白片段。

    13。打开片段的布局文件,即fragment_filters_list.xml,并添加RecyclerView元素。

    fragment_filters_list.xml

    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="info.androidhive.imagefilters.FiltersListFragment">

            android:id="@+id/recycler_view"

            android:layout_gravity="center_vertical"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:clipChildren="false"

            android:padding="4dp"

            android:scrollbars="none"/>

    相关文章

      网友评论

          本文标题:Android建筑图像过滤器,如Instagram(1)

          本文链接:https://www.haomeiwen.com/subject/aarnbftx.html