Repository: vivian8725118/CardView Branch: master Commit: cdb7681d5745 Files: 50 Total size: 81.0 KB Directory structure: gitextract_zrmng5pq/ ├── .gitignore ├── .idea/ │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── dictionaries/ │ │ └── vivian.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── inspectionProfiles/ │ │ ├── Project_Default.xml │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── vivian/ │ │ └── shadedemo/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── vivian/ │ │ │ └── shadedemo/ │ │ │ ├── MainActivity.java │ │ │ ├── adapter/ │ │ │ │ └── CardViewAdapter.java │ │ │ ├── anim/ │ │ │ │ └── RecyclerViewItemAnimator.java │ │ │ ├── drawable/ │ │ │ │ ├── BottomDrawable.java │ │ │ │ ├── CenterDrawable.java │ │ │ │ ├── CircleShadowDrawable.java │ │ │ │ └── TopDrawable.java │ │ │ ├── entity/ │ │ │ │ └── Card.java │ │ │ ├── util/ │ │ │ │ ├── CardThemeUtil.java │ │ │ │ ├── ScreenShotUtil.java │ │ │ │ └── ScreenSizeUtil.java │ │ │ ├── view/ │ │ │ │ ├── ShadowView.java │ │ │ │ └── TopShadowView.java │ │ │ └── widget/ │ │ │ └── CardView.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── circle.xml │ │ │ └── circle_retangle.xml │ │ ├── layout/ │ │ │ ├── activity_main_recyclerview.xml │ │ │ ├── card.xml │ │ │ └── icons.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── vivian/ │ └── shadedemo/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/dictionaries/vivian.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/inspectionProfiles/Project_Default.xml ================================================ ================================================ FILE: .idea/inspectionProfiles/profiles_settings.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ Abstraction issuesJava Android Android Lint Class metricsJava Code style issuesJava Declaration redundancyJava Error handlingJava Groovy Java Method MetricsGroovy Method metricsJava Naming ConventionsGroovy Naming conventionsJava Security issuesJava 1.8 ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # CardView 类似小票效果的卡片列表,并且可以修改阴影颜色 # Usage 可以直接引用CardView ``` CardView cardView=new CardView(context); //修改CardView的主题颜色 cardView.changeTheme(0xff01a3a1); ``` # 原理 用paint.setShadowLayer来设置阴影颜色和尺寸,进行阴影绘制,用path来画出drawable的形状。 封装了changeTheme的方法,可以直接设置主题的颜色。 本示例中为了更好地实现效果,用了```TopDrawable、CenterDrawable、BottomDrawable```三部分拼接而成,也可以用来实现打印小票的视觉效果。另外添加了一个类似FloatingButton效果的```CircleShadowDrawable```。 # 效果图
================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "24.0.3" defaultConfig { applicationId "com.vivian.shadedemo" minSdkVersion 14 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.0.0' testCompile 'junit:junit:4.12' compile 'com.android.support:recyclerview-v7:25.0.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/vivian/Documents/android-sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/vivian/shadedemo/ExampleInstrumentedTest.java ================================================ package com.vivian.shadedemo; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.vivian.shadedemo", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/vivian/shadedemo/MainActivity.java ================================================ package com.vivian.shadedemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.widget.LinearLayout; import com.vivian.shadedemo.adapter.CardViewAdapter; import com.vivian.shadedemo.entity.Card; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { RecyclerView mRecyclerView; LinearLayoutManager mLayoutManager; CardViewAdapter mAdapter; List mList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initViewRecyclerView(); } public void initViewRecyclerView() { LinearLayout view = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.activity_main_recyclerview, null); setContentView(view); mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new CardViewAdapter(this); mRecyclerView.setAdapter(mAdapter); //模拟数据 for (int i = 0; i < 10; i++) { Card card = new Card(); String content = "刘雯、何穗、奚梦瑶、雎晓雯......在公开的 52 位模特名单里,一下子出现了四位中国模特。要知道,一年一度的顶级维密大秀可是为“国际超模”四个字加持的大秀啊。接下来,时间将印证这四位模特的未来身价和职业生涯。"; card.setContent(content); mList.add(card); } mAdapter.setData(mList); mAdapter.notifyDataSetChanged(); } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/adapter/CardViewAdapter.java ================================================ package com.vivian.shadedemo.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.vivian.shadedemo.R; import com.vivian.shadedemo.entity.Card; import com.vivian.shadedemo.widget.CardView; import java.util.List; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class CardViewAdapter extends RecyclerView.Adapter { List mData; Context mContext; int[] colors = {0xff01a3a1, 0xff91bbeb, 0xff01b1bf, 0xffff9d62, 0xff2d3867, 0xffee697e};//颜色组 public void setData(List list) { this.mData = list; } public CardViewAdapter(Context context) { mContext = context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = new CardView(mContext); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.content.setText(mData.get(position).getContent()); holder.comment.setText(mData.get(position).getComment()); holder.comment.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { mData.get(holder.getAdapterPosition()).setComment(charSequence.toString()); } @Override public void afterTextChanged(Editable editable) { } }); //设置颜色变化 ((CardView) holder.itemView).changeTheme(colors[position % 6]); } @Override public int getItemCount() { return mData.size(); } static class ViewHolder extends RecyclerView.ViewHolder { RelativeLayout top; ImageView center; EditText comment; TextView content; ViewHolder(View view) { super(view); top = (RelativeLayout) view.findViewById(R.id.top); center = (ImageView) view.findViewById(R.id.center); comment = (EditText) view.findViewById(R.id.comment); content = (TextView) view.findViewById(R.id.content); } } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/anim/RecyclerViewItemAnimator.java ================================================ package com.vivian.shadedemo.anim; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class RecyclerViewItemAnimator extends RecyclerView.ItemAnimator{ @Override public boolean animateDisappearance(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) { return false; } @Override public boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { return false; } @Override public boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { return false; } @Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) { return false; } @Override public void runPendingAnimations() { } @Override public void endAnimation(RecyclerView.ViewHolder item) { } @Override public void endAnimations() { } @Override public boolean isRunning() { return false; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/drawable/BottomDrawable.java ================================================ package com.vivian.shadedemo.drawable; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class BottomDrawable extends Drawable { Paint paint; Paint paintShadow; Path path; RectF rectF; int x = 20; int y = 0; int color = 0x2601a3a1; public BottomDrawable() { paint = new Paint(); paint.setColor(Color.WHITE); paint.setAntiAlias(true); paintShadow = new Paint(); paintShadow.setColor(Color.WHITE); paintShadow.setAntiAlias(true); path = new Path(); } public void setColor(int color) { this.color = color; // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paintShadow.setShadowLayer(21, 0, 10, color & 0x26ffffff); invalidateSelf(); } @Override public void draw(Canvas canvas) { canvas.drawPath(path, paintShadow); canvas.drawRect(x, y, getBounds().width() - x, y + 21, paint); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); path.reset(); rectF = new RectF(x, y, bounds.width() - x, y + bounds.height() - 21); path.addRoundRect(rectF, new float[]{0, 0, 0, 0, 10, 10, 10, 10}, Path.Direction.CW); } @Override public void setAlpha(int i) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/drawable/CenterDrawable.java ================================================ package com.vivian.shadedemo.drawable; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathEffect; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class CenterDrawable extends Drawable { Paint paint; Paint paintShadow; Paint paintLine; Path path; Bitmap bitmap; int color=0x2601a3a1; PathEffect effects = new DashPathEffect(new float[]{ 6, 8}, 1); public CenterDrawable(Bitmap bitmap) { this.bitmap=bitmap; paint = new Paint(); paint.setColor(Color.WHITE); paint.setAntiAlias(true); paintShadow = new Paint(); paintShadow.setColor(Color.WHITE); paintShadow.setAntiAlias(true); path = new Path(); paintLine = new Paint(); paintLine.setColor(Color.GRAY); paintLine.setAntiAlias(true); paintLine.setPathEffect(effects); paintLine.setStyle(Paint.Style.FILL); paintLine.setStrokeWidth(1); } public void setColor(int color){ this.color=color; // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paintShadow.setShadowLayer(21, 0, 10, color&0x26ffffff); invalidateSelf(); } @Override public void onBoundsChange(Rect bounds){ int x = bounds.left + 20; int y = bounds.top; path.reset(); path.moveTo(x, y); path.lineTo(bounds.right - x, y); path.quadTo(bounds.right - x - bounds.bottom * 0.8f, bounds.bottom / 2, bounds.right - x, bounds.bottom); path.lineTo(x, bounds.bottom); path.quadTo(x + bounds.bottom * 0.8f, bounds.bottom / 2, x, y); path.close(); } @Override public void draw(Canvas canvas) { int x = getBounds().left + 20; int y = getBounds().top; //画背景 canvas.drawPath(path, paintShadow); //画一半虚线 canvas.drawLine(x+getBounds().bottom*0.8f, getBounds().bottom/2, (getBounds().right - x - getBounds().bottom * 0.8f)/2-bitmap.getWidth(), getBounds().bottom/2, paintLine); //画引号 canvas.drawBitmap(bitmap,getBounds().right/2-bitmap.getWidth(),y,new Paint()); //画另一半虚线 canvas.drawLine((getBounds().right - x - getBounds().bottom * 0.8f)/2+bitmap.getWidth()*3/2, getBounds().bottom/2, getBounds().right - x - getBounds().bottom * 0.8f, getBounds().bottom/2, paintLine); } @Override public void setAlpha(int i) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/drawable/CircleShadowDrawable.java ================================================ package com.vivian.shadedemo.drawable; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.drawable.Drawable; import com.vivian.shadedemo.R; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class CircleShadowDrawable extends Drawable { private Bitmap bitmap = null; Context mContext; // 建立Paint 物件 Paint paint3 = new Paint(); public CircleShadowDrawable(Context context){ mContext=context; // bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.top_selector); } @Override public void draw(Canvas canvas) { paint3.setColor(Color.RED); // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paint3.setShadowLayer(300, 0, 0, Color.RED); // 实心矩形& 其阴影 canvas.drawCircle(400, 500, 300, paint3); } @Override public void setAlpha(int i) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return 0; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/drawable/TopDrawable.java ================================================ package com.vivian.shadedemo.drawable; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class TopDrawable extends Drawable { Paint paint; Paint paintShadow; Path path; RectF rectF; int x = 20; int y = 20; int color = 0x2601a3a1; public TopDrawable() { paint = new Paint(); paint.setColor(Color.WHITE); paint.setAntiAlias(true); paintShadow = new Paint(); paintShadow.setColor(Color.WHITE); paintShadow.setAntiAlias(true); path = new Path(); } public void setColor(int color) { this.color = color; // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paintShadow.setShadowLayer(21, 0, 10, color & 0x26ffffff);//0x2600A4A0 invalidateSelf(); } @Override public void draw(Canvas canvas) { canvas.drawPath(path, paintShadow); canvas.drawRect(x, getBounds().height(), getBounds().width() - x, getBounds().height() + 21, paint); } @Override public void onBoundsChange(Rect bounds) { path.reset(); rectF = new RectF(x, y, bounds.width() - x, bounds.height()); path.addRoundRect(rectF, new float[]{6, 6, 6, 6, 0, 0, 0, 0}, Path.Direction.CW); } @Override public void setAlpha(int i) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/entity/Card.java ================================================ package com.vivian.shadedemo.entity; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class Card { private String content; private String comment = ""; public String getComment() { return comment; } public void setComment(String comment) { this.comment = comment; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/util/CardThemeUtil.java ================================================ package com.vivian.shadedemo.util; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.v4.content.ContextCompat; import android.util.Log; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class CardThemeUtil { public Drawable colorMatrixFilter(Context context,int res, int color) { int r = Color.red(color); int g = Color.green(color); int b = Color.blue(color); //定义RGBA的矩阵 float[] src = new float[]{ 0, 0, 0, 0, r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 1, 0}; ColorMatrix matrix = new ColorMatrix(src); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix); Drawable drawable = ContextCompat.getDrawable(context,res); drawable.setColorFilter(filter); return drawable; } /** * Usage:很耗性能 * topLayout.setBackground(colorFilter(R.drawable.top)); * centerLayout.setBackground(colorFilter(R.drawable.center)); * bottomLayout.setBackground(colorFilter(R.drawable.bottom)); * * @param res * @return */ public Drawable colorFilter(Context context,int res) { Drawable drawable = context.getDrawable(res); Bitmap bitmap = drawableToBitmap(drawable); int width = bitmap.getWidth(); int height = bitmap.getHeight(); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int color = bitmap.getPixel(x, y); if (color != 0xffffffff) { Log.e("color:", "alpha:" + Color.alpha(color)); Log.e("color:", "red:" + Color.red(color)); Log.e("color:", "green:" + Color.green(color)); Log.e("color:", "blue:" + Color.blue(color) + "\n"); int alpha = Color.alpha(color); int color2 = Color.argb(alpha, 249, 39, 54); pixels[y * width + x] = color2; } else { pixels[y * width + x] = 0xffffffff; } } } bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_4444); Drawable d = new BitmapDrawable(context.getResources(), bitmap); return d; } public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/util/ScreenShotUtil.java ================================================ package com.vivian.shadedemo.util; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.view.View; import android.view.ViewGroup; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class ScreenShotUtil { /** * 截图 * * @param view * @return */ public static Bitmap convertViewToBitmap(View view) { view.setDrawingCacheEnabled(true); view.buildDrawingCache(); //启用DrawingCache并创建位图 Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); //创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收 view.setDrawingCacheEnabled(false); //禁用DrawingCahce否则会影响性能 view.destroyDrawingCache(); return bitmap; } /** * 截取长图 * * @param view * @return */ public static Bitmap getScrollViewBitmap(ViewGroup view) { int h = 0; for (int i = 0; i < view.getChildCount(); i++) { h += view.getChildAt(i).getHeight(); } Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), h, Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas(bitmap); view.draw(canvas); return bitmap; } /** * 保存到本地图库 * * @param context * @param bitmap */ public static void saveImageToGallery(Context context, Bitmap bitmap) { // Create screenshot directory if it doesn't exist String dirName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "TMT"; File fileDir = new File(dirName); if (!fileDir.exists()) { fileDir.mkdir(); } long mImageTime = System.currentTimeMillis();//取当前系统时间 // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds // for DATE_TAKEN long dateSeconds = mImageTime / 1000; String mImageFileName = dateSeconds + ".png"; //以保存时间命名 String mImageFilePath = dirName + File.separator + mImageFileName; //注意这里的mImageFilePath是: 目录名称+文件名 int mImageWidth = bitmap.getWidth(); int mImageHeight = bitmap.getHeight(); // Save the screenshot to the MediaStore ContentValues values = new ContentValues(); ContentResolver resolver = context.getContentResolver(); values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath); values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName); values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName); values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime); values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds); values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds); values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png"); values.put(MediaStore.Images.ImageColumns.WIDTH, mImageWidth); values.put(MediaStore.Images.ImageColumns.HEIGHT, mImageHeight); Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); try { OutputStream out = resolver.openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); out.flush(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // update file size in the database values.clear(); values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length()); resolver.update(uri, values, null, null); } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/util/ScreenSizeUtil.java ================================================ package com.vivian.shadedemo.util; import android.app.Activity; import android.content.Context; import android.util.DisplayMetrics; import android.util.TypedValue; public class ScreenSizeUtil { private static DisplayMetrics metrics; /** * 根据绝对尺寸得到相对尺寸,在不同的分辨率设备上有一致的显示效果, dip->pix * * @param context * @param givenAbsSize * 绝对尺寸 * @return */ public static int getSizeByGivenAbsSize(Context context, int givenAbsSize) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, givenAbsSize, context.getResources().getDisplayMetrics()); } private static DisplayMetrics getDisplayMetrics(Context context) { if (metrics != null) { return metrics; } metrics = new DisplayMetrics(); ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics); return metrics; } public static int getScreenWidth(Context context) { return getDisplayMetrics(context).widthPixels; // 屏幕宽度(像素) } public static int getScreenHeight(Context context) { return getDisplayMetrics(context).heightPixels;// 屏幕高度(像素) } public static float getScreenDensity(Context context) { return getDisplayMetrics(context).density; // 屏幕密度(0.75 / 1.0 / 1.5) } public static int getScreenDensityDpi(Context context) { return getDisplayMetrics(context).densityDpi;// 屏幕密度DPI(120 / 160 / 240) } public static int Dp2Px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } public static int Px2Dp(Context context, float px) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (px / scale + 0.5f); } /** * 将px值转换为sp值,保证文字大小不变 * * @param pxValue * @return */ public static int px2sp(float pxValue, Context context) { return (int) (pxValue / context.getResources().getDisplayMetrics().scaledDensity + 0.5f); } /** * 将sp值转换为px值,保证文字大小不变 * * @param spValue * @return */ public static int sp2px(float spValue, Context context) { return (int) (spValue * context.getResources().getDisplayMetrics().scaledDensity + 0.5f); } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/view/ShadowView.java ================================================ package com.vivian.shadedemo.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.View; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class ShadowView extends View { // private Bitmap bitmap = null; public ShadowView(Context context) { super(context); // bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.top); setLayerType(View.LAYER_TYPE_SOFTWARE, null);//没有这句不显示 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 建立Paint 物件 Paint paint3 = new Paint(); paint3.setColor(Color.WHITE); paint3.setAntiAlias(true); // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paint3.setShadowLayer(21, 0, 10, 0x2600A4A0); // 实心矩形& 其阴影 canvas.drawCircle(400, 500, 96, paint3); } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/view/TopShadowView.java ================================================ package com.vivian.shadedemo.view; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/11/30. */ public class TopShadowView extends View { int width = 400; int height = 400; int x=50; int y=50; public TopShadowView(Context context) { super(context); init(context); } public TopShadowView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TopShadowView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public TopShadowView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } public void init(Context context) { setLayerType(View.LAYER_TYPE_SOFTWARE, null);//没有这句不显示 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); width=getWidth()-x*2; height=getHeight()-y*2; Paint paint = new Paint(); paint.setColor(Color.WHITE); paint.setAntiAlias(true); // 建立Paint 物件 Paint paint3 = new Paint(); paint3.setColor(Color.WHITE); paint3.setAntiAlias(true); // 设定阴影(柔边, X 轴位移, Y 轴位移, 阴影颜色) paint3.setShadowLayer(21, 0, 10, 0x2600A4A0); // 实心矩形& 其阴影 RectF rectF = new RectF(100, 100, width, height); Path path=new Path(); path.addRoundRect(x,y,width,height,new float[]{10,10,10,10,0,0,0,0}, Path.Direction.CW); canvas.drawPath(path, paint3); canvas.drawRect(x,height,width,height+21,paint); } } ================================================ FILE: app/src/main/java/com/vivian/shadedemo/widget/CardView.java ================================================ package com.vivian.shadedemo.widget; import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import com.vivian.shadedemo.R; import com.vivian.shadedemo.drawable.BottomDrawable; import com.vivian.shadedemo.drawable.CenterDrawable; import com.vivian.shadedemo.drawable.TopDrawable; import com.vivian.shadedemo.util.ScreenSizeUtil; /** * * _ _ * * __ _(_)_ _(_) __ _ _ __ * * \ \ / / \ \ / / |/ _` | '_ \ * * \ V /| |\ V /| | (_| | | | | * * \_/ |_| \_/ |_|\__,_|_| |_| *

* Created by vivian on 2016/12/1. */ public class CardView extends LinearLayout { RelativeLayout topLayout; ImageView centerLayout; TextView content; EditText comment; TopDrawable topDrawable; CenterDrawable centerDrawable; BottomDrawable bottomDrawable; GradientDrawable myGrad; public CardView(Context context) { super(context); init(context); } public CardView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CardView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } public CardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } public void init(final Context context) { LinearLayout layout = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.card, null); layout.setLayoutParams(new LinearLayout.LayoutParams(ScreenSizeUtil.Dp2Px(getContext(), 285), LayoutParams.WRAP_CONTENT)); layout.setLayerType(View.LAYER_TYPE_SOFTWARE, null);//没有这句不显示 topLayout = (RelativeLayout) layout.findViewById(R.id.top); centerLayout = (ImageView) layout.findViewById(R.id.center); content = (TextView) layout.findViewById(R.id.content); comment = (EditText) layout.findViewById(R.id.comment); myGrad = (GradientDrawable) content.getBackground(); topDrawable = new TopDrawable(); topLayout.setBackground(topDrawable); centerDrawable = new CenterDrawable(BitmapFactory.decodeResource(getResources(), R.drawable.quote)); centerLayout.setBackground(centerDrawable); bottomDrawable = new BottomDrawable(); comment.setBackground(bottomDrawable); addView(layout); } public void changeTheme(final int color) { //文字背景颜色 myGrad.setColor(color); //顶部阴影颜色 topDrawable.setColor(color); // 中部阴影颜色 centerDrawable.setColor(color); // //底部阴影颜色 bottomDrawable.setColor(color); } @Override public boolean hasOverlappingRendering() { return false; } } ================================================ FILE: app/src/main/res/drawable/circle.xml ================================================ ================================================ FILE: app/src/main/res/drawable/circle_retangle.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main_recyclerview.xml ================================================ ================================================ FILE: app/src/main/res/layout/card.xml ================================================ ================================================ FILE: app/src/main/res/layout/icons.xml ================================================ ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #3F51B5 #303F9F #FF4081 ================================================ FILE: app/src/main/res/values/dimens.xml ================================================ 16dp 16dp ================================================ FILE: app/src/main/res/values/strings.xml ================================================ ShadeDemo ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/main/res/values-w820dp/dimens.xml ================================================ 64dp ================================================ FILE: app/src/test/java/com/vivian/shadedemo/ExampleUnitTest.java ================================================ package com.vivian.shadedemo; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ include ':app'