├── .gitignore ├── .idea ├── assetWizardSettings.xml ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml └── runConfigurations.xml ├── JavaTimeLine ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── github │ │ └── rangaofei │ │ └── javatimeline │ │ ├── AdapterUtil.java │ │ ├── AnnotationProcessor.java │ │ ├── TimeLineContext.java │ │ ├── processor │ │ ├── AnchorProcessor.java │ │ ├── DividerProcessor.java │ │ ├── ImageViewProcessor.java │ │ ├── TextViewProcessor.java │ │ ├── TimeLineProcess.java │ │ ├── TimeLineProcessor.java │ │ └── ViewProcessor.java │ │ ├── utils │ │ └── StringUtil.java │ │ └── viewattr │ │ ├── AnchorInfo.java │ │ ├── ImageViewAttr.java │ │ └── TextViewAttr.java │ └── test │ └── java │ └── io │ └── github │ └── rangaofei │ └── javatimeline │ └── utils │ └── StringUtilTest.java ├── Pics ├── Screenshot_1532504922.png ├── SimpleStepViewUnnormal.png ├── StepViewAnim.gif ├── StepViewCustomDrawable.png ├── StepViewDiff.png ├── StepView_anim.webm ├── eight.png ├── muke.mp4 ├── muke.png ├── simple_stepview_all.png ├── simple_stepview_left.png ├── taobao.png └── wechat.jpg ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── rangaofei │ │ └── timeline │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── rangaofei │ │ │ └── timeline │ │ │ ├── BaseModel.java │ │ │ ├── MainActivity.java │ │ │ ├── MukeBean.java │ │ │ ├── MukeFragment.java │ │ │ ├── StepViewModel.java │ │ │ ├── SwipeAdapter.java │ │ │ ├── SwipeRecyclerViewActivity.java │ │ │ ├── TaobaoFragment.java │ │ │ └── TimeLineAdapter.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_rect.xml │ │ ├── dot_grey.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_offline_pin_black_24dp.xml │ │ ├── ic_order.xml │ │ └── withpadding.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_swipe_recycler_view.xml │ │ ├── fragment_blank.xml │ │ ├── fragment_muke.xml │ │ ├── item.xml │ │ ├── item_key.xml │ │ ├── item_muke.xml │ │ ├── item_swipe.xml │ │ └── item_value.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── io │ └── github │ └── rangaofei │ └── timeline │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libannotations ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── io │ └── github │ └── rangaofei │ └── libannotations │ ├── TimeConfig.java │ ├── TimeLine.java │ ├── TimeLineAnchor.java │ ├── TimeLineDividerAdapter.java │ ├── TimeLineImageView.java │ ├── TimeLineTextView.java │ └── TimeLineView.java ├── sakatimeline ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── rangaofei │ │ └── sakatimeline │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── rangaofei │ │ │ └── sakatimeline │ │ │ ├── TimeLineView.java │ │ │ ├── adapter │ │ │ ├── AbstractTimeLineAdapter.java │ │ │ ├── BaseViewHolder.java │ │ │ ├── ItemClickListener.java │ │ │ └── ItemTypeStrategy.java │ │ │ ├── config │ │ │ ├── IndexTextConfig.java │ │ │ ├── StepViewConfig.java │ │ │ ├── StrokeType.java │ │ │ └── TimeLineConfig.java │ │ │ ├── customlayoutmanager │ │ │ ├── PerfectLinearLayoutManager.java │ │ │ ├── ScrollbarHelper.java │ │ │ ├── TimeLineGridLayoutManager.java │ │ │ ├── TimeLineLayoutManager.java │ │ │ └── ViewBoundsCheck.java │ │ │ ├── divider │ │ │ ├── BaseDivider.java │ │ │ ├── DividerLayoutAdapter.java │ │ │ ├── ItemTouchUIUtilImpl.java │ │ │ ├── LeftOnlyDivider.java │ │ │ ├── LeftRightDivider.java │ │ │ ├── RightOnlyDivider.java │ │ │ ├── SakaItemTouchHelper.java │ │ │ ├── SingleStepViewDivider.java │ │ │ ├── SlideTouchHelperCallBack.java │ │ │ ├── SwipeBehavior.java │ │ │ ├── TimeLineDivider.java │ │ │ └── TimeLineType.java │ │ │ ├── exception │ │ │ ├── BaseException.java │ │ │ ├── ExceptionMessage.java │ │ │ ├── TimeLineException.java │ │ │ └── TimeLineViewException.java │ │ │ ├── indexdecoration │ │ │ └── IndexDecoration.java │ │ │ ├── proxy │ │ │ ├── TextViewInterface.java │ │ │ ├── TextViewProxy.java │ │ │ ├── TextViewProxyHandler.java │ │ │ ├── ViewInterface.java │ │ │ ├── ViewProxy.java │ │ │ └── ViewProxyHandler.java │ │ │ ├── snaphelper │ │ │ └── ScaleSnapHelper.java │ │ │ ├── timelineedgeeffect │ │ │ ├── CustomEdgeEffectFactory.java │ │ │ └── SakaEffectEdge.java │ │ │ └── util │ │ │ ├── AndroidUtil.java │ │ │ ├── ExceptionUtil.java │ │ │ └── ProxyUtil.java │ └── res │ │ ├── drawable │ │ ├── drawable_null.xml │ │ ├── ic_order.xml │ │ └── order.png │ │ ├── layout │ │ └── null_view.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── io │ └── github │ └── rangaofei │ └── sakatimeline │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | /.idea 12 | -------------------------------------------------------------------------------- /.idea/assetWizardSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /JavaTimeLine/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /JavaTimeLine/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | apply plugin: 'com.jfrog.bintray' 4 | 5 | 6 | 7 | version = project.ext.version 8 | 9 | dependencies { 10 | implementation fileTree(include: ['*.jar'], dir: 'libs') 11 | // implementation 'com.google.auto:auto-common:0.10' 12 | implementation 'com.google.auto.service:auto-service:1.0-rc3' 13 | implementation 'com.squareup:javapoet:1.9.0' 14 | testImplementation 'junit:junit:4.12' 15 | implementation 'com.rangaofei:libannotations:0.2.1' 16 | } 17 | 18 | //sourceCompatibility = "1.8" 19 | //targetCompatibility = "1.8" 20 | 21 | 22 | 23 | def siteUrl = 'https://github.com/rangaofei/TimeLine' 24 | def gitUrl = 'https://github.com/rangaofei/TimeLine' 25 | group = "com.rangaofei" 26 | 27 | install { 28 | repositories.mavenInstaller { 29 | // This generates POM.xml with proper parameters 30 | pom { 31 | project { 32 | packaging 'jar' 33 | name 'JavaTimeLine' 34 | // #CONFIG# // project title 35 | url siteUrl 36 | // Set your license 37 | licenses { 38 | license { 39 | name 'The Apache Software License, Version 2.0' 40 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 41 | } 42 | } 43 | developers { 44 | developer { 45 | id 'tudou' 46 | // #CONFIG# // your user id (you can write your nickname) 47 | name 'rangaofei' 48 | // #CONFIG# // your user name 49 | email '876038559@qq.com' 50 | // #CONFIG# // your email 51 | } 52 | } 53 | scm { 54 | connection gitUrl 55 | developerConnection gitUrl 56 | url siteUrl 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | task sourcesJar(type: Jar) { 64 | classifier = 'sources' 65 | from sourceSets.main.allSource 66 | } 67 | 68 | 69 | 70 | task javadocJar(type: Jar, dependsOn: javadoc) { 71 | classifier = 'javadoc' 72 | from javadoc.destinationDir 73 | } 74 | 75 | artifacts { 76 | archives sourcesJar 77 | } 78 | 79 | Properties properties = new Properties() 80 | boolean isHasFile = false 81 | if (project.rootProject.file('local.properties') != null) { 82 | isHasFile = true 83 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 84 | } 85 | bintray { 86 | user = isHasFile ? properties.getProperty("bintray.user") : System.getenv("bintray.user") 87 | key = isHasFile ? properties.getProperty("bintray.apikey") : System.getenv("bintray.apikey") 88 | configurations = ['archives'] 89 | pkg { 90 | repo = "maven" 91 | name = "JavaTimeLine" 92 | websiteUrl = siteUrl 93 | vcsUrl = gitUrl 94 | licenses = ["Apache-2.0"] 95 | publish = true 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/AdapterUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.CodeBlock; 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.squareup.javapoet.ParameterSpec; 7 | import com.squareup.javapoet.ParameterizedTypeName; 8 | import com.squareup.javapoet.TypeName; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | import javax.lang.model.element.Element; 15 | import javax.lang.model.element.Modifier; 16 | 17 | import io.github.rangaofei.javatimeline.viewattr.ImageViewAttr; 18 | import io.github.rangaofei.libannotations.TimeLine; 19 | import io.github.rangaofei.libannotations.TimeLineDividerAdapter; 20 | 21 | public class AdapterUtil { 22 | 23 | public static String generateAdapterName(Element element, String className) { 24 | String generatedName = element.getAnnotation(TimeLine.class).name().trim(); 25 | if (generatedName.equals("")) { 26 | generatedName = className.trim() + "Adapter"; 27 | } 28 | 29 | return generatedName; 30 | } 31 | 32 | public static String generateDividerAdapterName(Element element, String className) { 33 | String generatedName = element.getAnnotation(TimeLineDividerAdapter.class).value().trim(); 34 | if (generatedName.equals("")) { 35 | generatedName = className.trim() + "DividerAdapter"; 36 | } 37 | return generatedName; 38 | } 39 | 40 | public static MethodSpec constructorMethod(String fullClassName) { 41 | ClassName list = ClassName.get(java.util.List.class); 42 | TypeName listT = ParameterizedTypeName.get(list, ClassName.bestGuess(fullClassName)); 43 | ParameterSpec constructorParameter = ParameterSpec.builder(listT, "list") 44 | .build(); 45 | return MethodSpec.constructorBuilder() 46 | .addModifiers(Modifier.PUBLIC) 47 | .addParameter(constructorParameter) 48 | .addStatement("super(list)") 49 | .build(); 50 | } 51 | 52 | public static MethodSpec generateOverRideIdMethod(String layoutId, String methodName) { 53 | return MethodSpec.methodBuilder(methodName) 54 | .addModifiers(Modifier.PUBLIC) 55 | .addAnnotation(Override.class) 56 | .returns(int.class) 57 | .addStatement("return $L", layoutId) 58 | .build(); 59 | 60 | } 61 | 62 | public static MethodSpec generateBindMethod(String methodName, 63 | String fullClassName, 64 | CodeBlock... codeBlocks) { 65 | ParameterSpec one = ParameterSpec 66 | .builder(ClassName.bestGuess("io.github.rangaofei.sakatimeline.adapter.BaseViewHolder"), "holder") 67 | .build(); 68 | ParameterSpec two = ParameterSpec 69 | .builder(ClassName.bestGuess(fullClassName), "data") 70 | .build(); 71 | 72 | MethodSpec.Builder bindItemMethod = MethodSpec.methodBuilder(methodName) 73 | .addModifiers(Modifier.PUBLIC) 74 | .addAnnotation(Override.class) 75 | .addParameters(Arrays.asList(one, two)); 76 | for (CodeBlock codeBlock : codeBlocks) { 77 | bindItemMethod.addCode(codeBlock); 78 | } 79 | return bindItemMethod.build(); 80 | 81 | } 82 | 83 | 84 | public static List getImageViewAttr(List imageViewAttrList, String holderName) { 85 | List codeBlockList = new ArrayList<>(); 86 | ClassName imageView = ClassName.bestGuess("android.widget.ImageView"); 87 | for (int i = 0; i < imageViewAttrList.size(); i++) { 88 | ImageViewAttr imageViewAttr = imageViewAttrList.get(i); 89 | String filedName = "imageView_" + i; 90 | CodeBlock codeBlock = CodeBlock.builder() 91 | .addStatement("$T $L = (($L)holder).itemView.findViewById($L)", 92 | imageView, filedName, holderName, imageViewAttr.getImageViewId()) 93 | .addStatement("$L.setImageResource(data.$L)", 94 | filedName, imageViewAttr.getImageSrc()) 95 | .build(); 96 | codeBlockList.add(codeBlock); 97 | } 98 | 99 | return codeBlockList; 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/AnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline; 2 | 3 | import com.google.auto.service.AutoService; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | import javax.annotation.processing.AbstractProcessor; 9 | import javax.annotation.processing.Completion; 10 | import javax.annotation.processing.ProcessingEnvironment; 11 | import javax.annotation.processing.Processor; 12 | import javax.annotation.processing.RoundEnvironment; 13 | import javax.lang.model.SourceVersion; 14 | import javax.lang.model.element.AnnotationMirror; 15 | import javax.lang.model.element.Element; 16 | import javax.lang.model.element.ElementKind; 17 | import javax.lang.model.element.ExecutableElement; 18 | import javax.lang.model.element.TypeElement; 19 | 20 | import io.github.rangaofei.javatimeline.TimeLineContext; 21 | import io.github.rangaofei.javatimeline.processor.DividerProcessor; 22 | import io.github.rangaofei.javatimeline.processor.TimeLineProcess; 23 | import io.github.rangaofei.javatimeline.processor.TimeLineProcessor; 24 | import io.github.rangaofei.libannotations.TimeLine; 25 | import io.github.rangaofei.libannotations.TimeLineDividerAdapter; 26 | 27 | /** 28 | * 注解处理入口类 29 | */ 30 | @AutoService(Processor.class) 31 | public class AnnotationProcessor extends AbstractProcessor { 32 | 33 | 34 | @Override 35 | public Set getSupportedOptions() { 36 | return super.getSupportedOptions(); 37 | } 38 | 39 | @Override 40 | public Set getSupportedAnnotationTypes() { 41 | Set s = new HashSet<>(); 42 | s.add(TimeLine.class.getCanonicalName()); 43 | return s; 44 | } 45 | 46 | @Override 47 | public SourceVersion getSupportedSourceVersion() { 48 | return super.getSupportedSourceVersion(); 49 | } 50 | 51 | @Override 52 | public synchronized void init(ProcessingEnvironment processingEnvironment) { 53 | super.init(processingEnvironment); 54 | TimeLineContext.filter = processingEnvironment.getFiler(); 55 | TimeLineContext.messager = processingEnvironment.getMessager(); 56 | TimeLineContext.elementUtil = processingEnvironment.getElementUtils(); 57 | TimeLineContext.typeUtil = processingEnvironment.getTypeUtils(); 58 | } 59 | 60 | @Override 61 | public Iterable getCompletions(Element element, AnnotationMirror annotationMirror, ExecutableElement executableElement, String s) { 62 | return super.getCompletions(element, annotationMirror, executableElement, s); 63 | } 64 | 65 | @Override 66 | public boolean process(Set set, RoundEnvironment roundEnvironment) { 67 | Set timeLineElements = roundEnvironment.getElementsAnnotatedWith(TimeLine.class); 68 | for (Element element : timeLineElements) { 69 | TimeLineContext.note(element.getSimpleName().toString()); 70 | if (element.getKind() != ElementKind.CLASS) { 71 | TimeLineContext.error("%s can not annotated with other than class", TimeLine.class.getName()); 72 | throw new RuntimeException("this element is not annotated with class"); 73 | } 74 | 75 | createTimeLineAdapter(element); 76 | } 77 | 78 | Set dividerElements = roundEnvironment.getElementsAnnotatedWith(TimeLineDividerAdapter.class); 79 | for (Element element : dividerElements) { 80 | TimeLineContext.note(element.getSimpleName().toString()); 81 | if (element.getKind() != ElementKind.FIELD) { 82 | throw new RuntimeException("not field"); 83 | } 84 | createDividerAdapter(element); 85 | } 86 | return true; 87 | } 88 | 89 | 90 | private void createTimeLineAdapter(Element element) { 91 | TimeLineProcess adapterProcessor = new TimeLineProcessor(element); 92 | adapterProcessor.processAnnotation(); 93 | } 94 | 95 | private void createDividerAdapter(Element element) { 96 | TimeLineProcess dividerProcessor = new DividerProcessor(element); 97 | dividerProcessor.processAnnotation(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/TimeLineContext.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline; 2 | 3 | 4 | import javax.annotation.processing.Filer; 5 | import javax.annotation.processing.Messager; 6 | import javax.lang.model.util.Elements; 7 | import javax.lang.model.util.Types; 8 | import javax.tools.Diagnostic; 9 | 10 | /** 11 | * 上下文的一个类。 12 | * 1. 打印日志 13 | */ 14 | public class TimeLineContext { 15 | 16 | public static final String TAG = "TimeLine"; 17 | private static final boolean DEBUG = true; 18 | public static Filer filter; 19 | 20 | public static Messager messager; 21 | 22 | public static Elements elementUtil; 23 | 24 | public static Types typeUtil; 25 | 26 | 27 | public static Filer getFilter() { 28 | if (filter == null) { 29 | throw new RuntimeException("filter is null"); 30 | } 31 | return filter; 32 | } 33 | 34 | 35 | public static Messager getMessager() { 36 | if (messager == null) { 37 | throw new RuntimeException("messager is null"); 38 | } 39 | return messager; 40 | } 41 | 42 | 43 | public static Elements getElementUtil() { 44 | if (elementUtil == null) { 45 | throw new RuntimeException("elementutil is null"); 46 | } 47 | return elementUtil; 48 | } 49 | 50 | 51 | public static void note(String msg, Object... args) { 52 | logMessage(Diagnostic.Kind.NOTE, msg, args); 53 | } 54 | 55 | public static void error(String msg, Object... args) { 56 | logMessage(Diagnostic.Kind.ERROR, msg, args); 57 | } 58 | 59 | public static void warning(String msg, Object... args) { 60 | logMessage(Diagnostic.Kind.WARNING, msg, args); 61 | } 62 | 63 | public static void other(String msg, Object... args) { 64 | logMessage(Diagnostic.Kind.OTHER, msg, args); 65 | } 66 | 67 | private static void logMessage(Diagnostic.Kind kind, String msg, Object... args) { 68 | if (DEBUG) { 69 | if (messager == null) { 70 | throw new RuntimeException("Messager is NULL"); 71 | } 72 | messager.printMessage(kind, String.format(msg, args)); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/AnchorProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.lang.model.element.Element; 7 | import javax.lang.model.util.ElementFilter; 8 | 9 | import io.github.rangaofei.javatimeline.TimeLineContext; 10 | import io.github.rangaofei.javatimeline.viewattr.AnchorInfo; 11 | import io.github.rangaofei.libannotations.TimeLineAnchor; 12 | 13 | public class AnchorProcessor implements TimeLineProcess { 14 | private Element element; 15 | private List anchorInfos; 16 | 17 | 18 | AnchorProcessor(Element element) { 19 | this.element = element; 20 | anchorInfos = new ArrayList<>(); 21 | } 22 | 23 | public List getAnchorInfos() { 24 | return anchorInfos; 25 | } 26 | 27 | @Override 28 | public void processAnnotation() { 29 | fillAnchorList(); 30 | } 31 | 32 | private void fillAnchorList() { 33 | if (this.element == null) { 34 | return; 35 | } 36 | for (Element e : ElementFilter.fieldsIn(element.getEnclosedElements())) { 37 | if (e.getAnnotation(TimeLineAnchor.class) != null) { 38 | AnchorInfo anchorInfo = new AnchorInfo(); 39 | TimeLineAnchor timeLineAnchor = e.getAnnotation(TimeLineAnchor.class); 40 | String filedName = e.getSimpleName().toString(); 41 | anchorInfo.setFieldName(filedName); 42 | for (String s : timeLineAnchor.value()) { 43 | anchorInfo.getAnchorIds().add(s); 44 | } 45 | anchorInfos.add(anchorInfo); 46 | } 47 | } 48 | 49 | if (anchorInfos.size() > 1) { 50 | throw new RuntimeException("one TimeLine Annotation class must not have more than one anchor"); 51 | } 52 | noteAnchorInfo(); 53 | } 54 | 55 | private void noteAnchorInfo() { 56 | if (this.anchorInfos.size() < 1) { 57 | return; 58 | } 59 | StringBuilder sb = new StringBuilder(); 60 | for (AnchorInfo anchorInfo : anchorInfos) { 61 | sb.append(anchorInfo.toString()); 62 | } 63 | TimeLineContext.note(sb.toString()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/DividerProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.JavaFile; 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.squareup.javapoet.ParameterSpec; 7 | import com.squareup.javapoet.ParameterizedTypeName; 8 | import com.squareup.javapoet.TypeName; 9 | import com.squareup.javapoet.TypeSpec; 10 | 11 | import java.io.IOException; 12 | import java.util.Arrays; 13 | 14 | import javax.lang.model.element.Element; 15 | import javax.lang.model.element.Modifier; 16 | 17 | import io.github.rangaofei.javatimeline.AdapterUtil; 18 | import io.github.rangaofei.javatimeline.TimeLineContext; 19 | 20 | public class DividerProcessor implements TimeLineProcess { 21 | private Element element; 22 | 23 | private String packageName; 24 | private String className; 25 | private String fullClassName; 26 | private String adapterName; 27 | 28 | private MethodSpec constructorMethod; 29 | 30 | public DividerProcessor(Element element) { 31 | this.element = element; 32 | } 33 | 34 | @Override 35 | public void processAnnotation() { 36 | getBasicField(); 37 | generateConstructor(); 38 | try { 39 | generateAdapter(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | private void getBasicField() { 46 | if (this.element == null) { 47 | throw new RuntimeException("element is null"); 48 | } 49 | TimeLineContext.note("---1"); 50 | packageName = TimeLineContext.elementUtil.getPackageOf(element).getQualifiedName().toString(); 51 | TimeLineContext.note("---2"); 52 | className = element.getSimpleName().toString(); 53 | TimeLineContext.note("---3"); 54 | fullClassName = packageName + "." + className; 55 | adapterName = AdapterUtil.generateDividerAdapterName(element, className); 56 | TimeLineContext.note("---4"); 57 | noteBasicInfo(); 58 | } 59 | 60 | private void generateConstructor() { 61 | ClassName list = ClassName.get(java.util.List.class); 62 | TypeName listT = ParameterizedTypeName.get(list, ClassName.bestGuess("android.graphics.drawable.Drawable")); 63 | ParameterSpec constructorParameter = ParameterSpec.builder(listT, "list") 64 | .build(); 65 | constructorMethod = MethodSpec.constructorBuilder() 66 | .addModifiers(Modifier.PUBLIC) 67 | .addParameter(constructorParameter) 68 | .addStatement("super(list)").build(); 69 | } 70 | 71 | private void generateAdapter() throws IOException { 72 | if (this.element == null) { 73 | return; 74 | } 75 | TypeSpec adapter = TypeSpec.classBuilder(adapterName) 76 | .superclass(ClassName.bestGuess("io.github.rangaofei.sakatimeline.divider.DividerLayoutAdapter")) 77 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 78 | .addMethods(Arrays.asList(constructorMethod)) 79 | .build(); 80 | JavaFile javaFile = JavaFile.builder(packageName, adapter) 81 | .build(); 82 | javaFile.writeTo(TimeLineContext.filter); 83 | } 84 | 85 | /** 86 | * 日志输出标注的类的基本信息 87 | */ 88 | private void noteBasicInfo() { 89 | TimeLineContext.note(">>>>packageName=%s", packageName); 90 | TimeLineContext.note(">>>>className=%s", className); 91 | TimeLineContext.note(">>>>fullClassName=%s", fullClassName); 92 | TimeLineContext.note(">>>>adapterName=%s", adapterName); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/ImageViewProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.CodeBlock; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import javax.lang.model.element.Element; 10 | import javax.lang.model.util.ElementFilter; 11 | 12 | import io.github.rangaofei.javatimeline.TimeLineContext; 13 | import io.github.rangaofei.javatimeline.utils.StringUtil; 14 | import io.github.rangaofei.javatimeline.viewattr.AnchorInfo; 15 | import io.github.rangaofei.javatimeline.viewattr.ImageViewAttr; 16 | import io.github.rangaofei.libannotations.TimeLineImageView; 17 | 18 | public class ImageViewProcessor implements TimeLineProcess { 19 | private Element element; 20 | private List anchorInfoList; 21 | private CodeBlock keyCodeBlock; 22 | private CodeBlock valueCodeBlock; 23 | private List imageViewAttrList; 24 | private ClassName imageView; 25 | 26 | public ImageViewProcessor(Element element, List anchorInfoList) { 27 | this.element = element; 28 | this.anchorInfoList = anchorInfoList; 29 | imageViewAttrList = new ArrayList<>(); 30 | imageView = ClassName.bestGuess("android.widget.ImageView"); 31 | } 32 | 33 | 34 | public CodeBlock getKeyCodeBlock() { 35 | return keyCodeBlock; 36 | } 37 | 38 | public CodeBlock getValueCodeBlock() { 39 | return valueCodeBlock; 40 | } 41 | 42 | @Override 43 | public void processAnnotation() { 44 | getImageViewAttr(); 45 | keyCodeBlock = generateImageViewCode(true); 46 | valueCodeBlock = generateImageViewCode(false); 47 | } 48 | 49 | private void getImageViewAttr() { 50 | if (this.element == null) { 51 | return; 52 | } 53 | for (Element e : ElementFilter.fieldsIn(element.getEnclosedElements())) { 54 | if (e.getAnnotation(TimeLineImageView.class) != null) { 55 | TimeLineImageView imageView = e.getAnnotation(TimeLineImageView.class); 56 | String filedName = e.getSimpleName().toString(); 57 | ImageViewAttr imageViewAttr = new ImageViewAttr(imageView.id(), filedName, imageView.key()); 58 | imageViewAttrList.add(imageViewAttr); 59 | 60 | } 61 | } 62 | } 63 | 64 | private CodeBlock generateImageViewCode(boolean isKey) { 65 | String holderName = null; 66 | if (isKey) { 67 | holderName = "KeyViewHolder"; 68 | } else { 69 | holderName = "ValueViewHolder"; 70 | } 71 | CodeBlock.Builder builder = CodeBlock.builder(); 72 | for (int i = 0; i < imageViewAttrList.size(); i++) { 73 | ImageViewAttr imageViewAttr = imageViewAttrList.get(i); 74 | String filedName = "imageView_" + i; 75 | if (isKey && !imageViewAttr.isKey()) { 76 | continue; 77 | } 78 | if (!isKey && imageViewAttr.isKey()) { 79 | continue; 80 | } 81 | if (!StringUtil.isResId(imageViewAttr.getImageViewId())) { 82 | logError("%s is not a standard resId format", imageViewAttr.getImageViewId()); 83 | } 84 | builder.addStatement("$T $L = (($L)holder).itemView.findViewById($L)", 85 | imageView, filedName, holderName, imageViewAttr.getImageViewId()); 86 | builder.addStatement("$L.setImageResource(data.$L)", filedName, imageViewAttr.getImageSrc()); 87 | // if (anchorInfoList.size() < 1 || 88 | // !anchorInfoList.get(0).getAnchorIds().contains(imageViewAttr.getImageViewId())) { 89 | // if (!imageViewAttr.getImageSrc().equals(TimeConfig.ID_NULL)) { 90 | // 91 | // } 92 | // 93 | // } else { 94 | // builder.beginControlFlow("if (data.$L)", anchorInfoList.get(0).getFieldName()); 95 | // if (!imageViewAttr.getStyleAnchorId().equals(TimeConfig.ID_NULL)) { 96 | // generateTextViewProxyCode(builder, filedName, imageViewAttr.getStyleAnchorId()); 97 | // } 98 | // builder.nextControlFlow("else "); 99 | // if (!imageViewAttr.getStyleId().equals(TimeConfig.ID_NULL)) { 100 | // generateTextViewProxyCode(builder, filedName, imageViewAttr.getStyleId()); 101 | // } 102 | // builder.endControlFlow(); 103 | // if (imageViewAttr.getTextString() != null) { 104 | // builder.addStatement("$L.setText(data.$L)", filedName, imageViewAttr.getTextString()); 105 | // } 106 | // } 107 | } 108 | return builder.build(); 109 | } 110 | 111 | private void logError(String msg, Object... args) { 112 | TimeLineContext.error(msg, args); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/TextViewProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.CodeBlock; 5 | import com.squareup.javapoet.ParameterizedTypeName; 6 | import com.squareup.javapoet.TypeName; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import javax.lang.model.element.Element; 12 | import javax.lang.model.util.ElementFilter; 13 | 14 | import io.github.rangaofei.javatimeline.viewattr.AnchorInfo; 15 | import io.github.rangaofei.javatimeline.viewattr.TextViewAttr; 16 | import io.github.rangaofei.libannotations.TimeConfig; 17 | import io.github.rangaofei.libannotations.TimeLineTextView; 18 | 19 | public class TextViewProcessor implements TimeLineProcess { 20 | 21 | private Element element; 22 | private List textViewAttrList; 23 | private CodeBlock keyCodeBlock; 24 | private CodeBlock valueCodeBlock; 25 | private ClassName textView; 26 | private List anchorInfoList; 27 | 28 | public TextViewProcessor(Element element, List anchorInfoList) { 29 | this.element = element; 30 | this.anchorInfoList = anchorInfoList; 31 | textViewAttrList = new ArrayList<>(); 32 | textView = ClassName.bestGuess("android.widget.TextView"); 33 | } 34 | 35 | public CodeBlock getKeyCodeBlock() { 36 | return keyCodeBlock; 37 | } 38 | 39 | public CodeBlock getValueCodeBlock() { 40 | return valueCodeBlock; 41 | } 42 | 43 | @Override 44 | public void processAnnotation() { 45 | getTextViewAttr(); 46 | keyCodeBlock = generateTextViewCodeBlock(true); 47 | valueCodeBlock = generateTextViewCodeBlock(false); 48 | } 49 | 50 | private void getTextViewAttr() { 51 | if (this.element == null) { 52 | return; 53 | } 54 | for (Element e : ElementFilter.fieldsIn(element.getEnclosedElements())) { 55 | if (e.getAnnotation(TimeLineTextView.class) != null) { 56 | TimeLineTextView textView = e.getAnnotation(TimeLineTextView.class); 57 | String filedName = e.getSimpleName().toString(); 58 | TextViewAttr textViewAttr = new TextViewAttr(filedName, textView); 59 | textViewAttrList.add(textViewAttr); 60 | 61 | } 62 | } 63 | 64 | } 65 | 66 | private CodeBlock generateTextViewCodeBlock(boolean isKey) { 67 | ClassName proxyUtil = ClassName.bestGuess("io.github.rangaofei.sakatimeline.util.ProxyUtil"); 68 | ClassName viewInterface = ClassName.get("io.github.rangaofei.sakatimeline.proxy", "ViewInterface"); 69 | TypeName viewInterfaceOfTextView = ParameterizedTypeName.get(viewInterface, textView); 70 | String holderName = null; 71 | if (isKey) { 72 | holderName = "KeyViewHolder"; 73 | } else { 74 | holderName = "ValueViewHolder"; 75 | } 76 | CodeBlock.Builder builder = CodeBlock.builder(); 77 | for (int i = 0; i < textViewAttrList.size(); i++) { 78 | TextViewAttr textViewAttr = textViewAttrList.get(i); 79 | String filedName = "textView_" + i; 80 | if (isKey && !textViewAttr.isKey()) { 81 | continue; 82 | } 83 | if (!isKey && textViewAttr.isKey()) { 84 | continue; 85 | } 86 | // builder.addStatement("$T $L = (($L)holder).itemView.findViewById($L)", 87 | // textView, filedName, holderName, textViewAttr.getTextViewId()); 88 | builder.addStatement("$T $L = $T.createView(($L)holder,$L)", 89 | viewInterfaceOfTextView, filedName, proxyUtil, holderName, textViewAttr.getTextViewId()); 90 | if (anchorInfoList.size() < 1 || 91 | !anchorInfoList.get(0).getAnchorIds().contains(textViewAttr.getTextViewId())) { 92 | if (!textViewAttr.getStyleId().equals(TimeConfig.ID_NULL)) { 93 | generateTextViewProxyCode(builder, filedName, textViewAttr.getStyleId()); 94 | } 95 | if (textViewAttr.getTextString() != null) { 96 | builder.addStatement("$L.getView().setText(data.$L)", filedName, textViewAttr.getTextString()); 97 | } 98 | } else { 99 | builder.beginControlFlow("if (data.$L)", anchorInfoList.get(0).getFieldName()); 100 | if (!textViewAttr.getStyleAnchorId().equals(TimeConfig.ID_NULL)) { 101 | generateTextViewProxyCode(builder, filedName, textViewAttr.getStyleAnchorId()); 102 | } 103 | builder.nextControlFlow("else "); 104 | if (!textViewAttr.getStyleId().equals(TimeConfig.ID_NULL)) { 105 | generateTextViewProxyCode(builder, filedName, textViewAttr.getStyleId()); 106 | } else { 107 | generateTextViewProxyCode(builder, filedName, "R.style.DefaultViewAttr"); 108 | } 109 | builder.endControlFlow(); 110 | if (textViewAttr.getTextString() != null) { 111 | builder.addStatement("$L.getView().setText(data.$L)", filedName, textViewAttr.getTextString()); 112 | } 113 | } 114 | } 115 | return builder.build(); 116 | } 117 | 118 | private void generateTextViewProxyCode(CodeBlock.Builder builder, String fieldName, String styleId) { 119 | // ClassName textViewInterface = 120 | // ClassName.bestGuess("io.github.rangaofei.sakatimeline.proxy.TextViewInterface"); 121 | // ClassName textViewProxy = 122 | // ClassName.bestGuess("io.github.rangaofei.sakatimeline.proxy.TextViewProxy"); 123 | // ClassName textViewProxyHandler = 124 | // ClassName.bestGuess("io.github.rangaofei.sakatimeline.proxy.TextViewProxyHandler"); 125 | // ClassName proxy = 126 | // ClassName.bestGuess("java.lang.reflect.Proxy"); 127 | // builder.addStatement("$T textProxy=($T)$T.newProxyInstance(\n" + 128 | // " TextViewInterface.class.getClassLoader(),\n" + 129 | // " new Class[]{TextViewInterface.class},\n" + 130 | // " new $T($L))", 131 | // textViewInterface, textViewInterface, proxy, textViewProxyHandler,fieldName); 132 | // builder.addStatement("textProxy.setTextAppearance($L.getContext(), $L)", 133 | // fieldName, styleId); 134 | 135 | builder.addStatement("$L.setStyle($L)", fieldName, styleId); 136 | 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/TimeLineProcess.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | public interface TimeLineProcess { 4 | void processAnnotation(); 5 | } 6 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/TimeLineProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.CodeBlock; 5 | import com.squareup.javapoet.JavaFile; 6 | import com.squareup.javapoet.MethodSpec; 7 | import com.squareup.javapoet.ParameterizedTypeName; 8 | import com.squareup.javapoet.TypeName; 9 | import com.squareup.javapoet.TypeSpec; 10 | 11 | import java.io.IOException; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | import javax.lang.model.element.Element; 16 | import javax.lang.model.element.Modifier; 17 | 18 | import io.github.rangaofei.javatimeline.AdapterUtil; 19 | import io.github.rangaofei.javatimeline.TimeLineContext; 20 | import io.github.rangaofei.javatimeline.viewattr.AnchorInfo; 21 | import io.github.rangaofei.libannotations.TimeLine; 22 | 23 | /** 24 | * 处理TimeLine注解 25 | */ 26 | public class TimeLineProcessor implements TimeLineProcess { 27 | private Element element; 28 | private String adapterName; 29 | private String packageName; 30 | private String className; 31 | private String fullClassName; 32 | private String keyLayoutId; 33 | private String valueLayoutId; 34 | private MethodSpec getKeyLayoutIdMethod; 35 | private MethodSpec getValueLayoutIdMethod; 36 | private MethodSpec bindKeyItemMethod; 37 | private MethodSpec bindValueItemMethod; 38 | private MethodSpec constructorMethod; 39 | 40 | private AnchorProcessor anchorProcessor; 41 | private List anchorInfoList; 42 | 43 | public TimeLineProcessor(Element element) { 44 | this.element = element; 45 | } 46 | 47 | @Override 48 | public void processAnnotation() { 49 | getBasicField(); 50 | generateConstructorMethod(); 51 | processAnchor(); 52 | generateLayoutIdMethod(); 53 | generateBindMethod(); 54 | try { 55 | generateAdapter(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | 61 | /** 62 | * 获取TimeLine标注的类的基本信息 63 | * 包名 64 | * 类名 65 | * 类的全路径名 66 | * 适配器名 67 | */ 68 | private void getBasicField() { 69 | if (this.element == null) { 70 | throw new RuntimeException("element is null"); 71 | } 72 | packageName = TimeLineContext.elementUtil.getPackageOf(element).getQualifiedName().toString(); 73 | className = element.getSimpleName().toString(); 74 | fullClassName = packageName + "." + className; 75 | adapterName = AdapterUtil.generateAdapterName(element, className); 76 | noteBasicInfo(); 77 | } 78 | 79 | private void generateConstructorMethod() { 80 | constructorMethod = AdapterUtil.constructorMethod(fullClassName); 81 | TimeLineContext.note(constructorMethod.toString()); 82 | } 83 | 84 | /** 85 | * 获取布局文件的id,并生成对应的复写方法 86 | */ 87 | private void generateLayoutIdMethod() { 88 | keyLayoutId = element.getAnnotation(TimeLine.class).keyLayoutId(); 89 | getKeyLayoutIdMethod = AdapterUtil.generateOverRideIdMethod(keyLayoutId, 90 | "getKeyLayoutId"); 91 | valueLayoutId = element.getAnnotation(TimeLine.class).valueLayoutId(); 92 | getValueLayoutIdMethod = AdapterUtil.generateOverRideIdMethod(valueLayoutId, 93 | "getValueLayoutId"); 94 | noteLayoutIdMethod(); 95 | } 96 | 97 | private void processAnchor() { 98 | anchorProcessor = new AnchorProcessor(element); 99 | anchorProcessor.processAnnotation(); 100 | anchorInfoList = anchorProcessor.getAnchorInfos(); 101 | } 102 | 103 | private void generateBindMethod() { 104 | 105 | TextViewProcessor textViewProcessor = new TextViewProcessor(element, anchorInfoList); 106 | textViewProcessor.processAnnotation(); 107 | CodeBlock keyTextViewCode = textViewProcessor.getKeyCodeBlock(); 108 | CodeBlock valueTextCode = textViewProcessor.getValueCodeBlock(); 109 | 110 | ImageViewProcessor imageViewProcessor = new ImageViewProcessor(element, anchorInfoList); 111 | imageViewProcessor.processAnnotation(); 112 | CodeBlock keyImageViewCode = imageViewProcessor.getKeyCodeBlock(); 113 | CodeBlock valueImageViewCode = imageViewProcessor.getValueCodeBlock(); 114 | 115 | bindKeyItemMethod = AdapterUtil.generateBindMethod("bindKeyItem", 116 | fullClassName, keyTextViewCode, keyImageViewCode); 117 | 118 | bindValueItemMethod = AdapterUtil.generateBindMethod("bindValueItem", 119 | fullClassName, valueTextCode, valueImageViewCode); 120 | } 121 | 122 | private void generateAdapter() throws IOException { 123 | TypeName superClass = ParameterizedTypeName 124 | .get(ClassName.bestGuess("io.github.rangaofei.sakatimeline.adapter.AbstractTimeLineAdapter"), 125 | ClassName.bestGuess(element.getSimpleName().toString())); 126 | TypeSpec adapter = TypeSpec.classBuilder(adapterName) 127 | .superclass(superClass) 128 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 129 | .addMethods(Arrays.asList(constructorMethod, getKeyLayoutIdMethod, getValueLayoutIdMethod, 130 | bindKeyItemMethod, bindValueItemMethod)) 131 | .build(); 132 | JavaFile javaFile = JavaFile.builder(packageName, adapter) 133 | .build(); 134 | javaFile.writeTo(TimeLineContext.filter); 135 | } 136 | 137 | /** 138 | * 日志输出标注的类的基本信息 139 | */ 140 | private void noteBasicInfo() { 141 | TimeLineContext.note(">>>>packageName=%s", packageName); 142 | TimeLineContext.note(">>>>className=%s", className); 143 | TimeLineContext.note(">>>>fullClassName=%s", fullClassName); 144 | TimeLineContext.note(">>>>adapterName=%s", adapterName); 145 | } 146 | 147 | private void noteLayoutIdMethod() { 148 | TimeLineContext.note(">>>>keyLayoutId:%s", getKeyLayoutIdMethod.toString()); 149 | TimeLineContext.note(">>>>valueLayoutId:%s", getValueLayoutIdMethod.toString()); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/processor/ViewProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.processor; 2 | 3 | import java.util.List; 4 | 5 | import javax.lang.model.element.Element; 6 | 7 | import io.github.rangaofei.javatimeline.viewattr.AnchorInfo; 8 | 9 | public class ViewProcessor implements TimeLineProcess { 10 | private Element element; 11 | private List anchorInfoList; 12 | 13 | public ViewProcessor(Element element, List anchorInfoList) { 14 | this.element = element; 15 | this.anchorInfoList = anchorInfoList; 16 | } 17 | 18 | @Override 19 | public void processAnnotation() { 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/utils/StringUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.utils; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public final class StringUtil { 7 | 8 | private static String layoutIdRegex = "^R\\.layout\\.[a-z_]+$"; 9 | private static String resIdRegex = "^R\\.id\\.[A-Za-z1-9_-]+$"; 10 | 11 | public static boolean isLayoutId(String src) { 12 | return isMatch(layoutIdRegex, src); 13 | } 14 | 15 | public static boolean isResId(String src) { 16 | return isMatch(resIdRegex, src); 17 | } 18 | 19 | private static boolean isMatch(String regex, String src) { 20 | Pattern pattern = Pattern.compile(regex); 21 | Matcher matcher = pattern.matcher(src); 22 | return matcher.matches(); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/viewattr/AnchorInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.viewattr; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class AnchorInfo { 8 | private String fieldName; 9 | private List anchorIds; 10 | 11 | public AnchorInfo() { 12 | anchorIds = new ArrayList<>(); 13 | } 14 | 15 | public AnchorInfo(String fieldName, List anchorIds) { 16 | this.fieldName = fieldName; 17 | this.anchorIds = anchorIds; 18 | } 19 | 20 | public String getFieldName() { 21 | return fieldName; 22 | } 23 | 24 | public void setFieldName(String fieldName) { 25 | this.fieldName = fieldName; 26 | } 27 | 28 | public List getAnchorIds() { 29 | return anchorIds; 30 | } 31 | 32 | public void setAnchorIds(List anchorIds) { 33 | this.anchorIds = anchorIds; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "AnchorInfo{" + 39 | "fieldName='" + fieldName + '\'' + 40 | ", anchorIds=" + Arrays.toString(anchorIds.toArray()) + 41 | '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/viewattr/ImageViewAttr.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.viewattr; 2 | 3 | public class ImageViewAttr { 4 | private String imageViewId; 5 | private String imageSrc; 6 | private boolean isKey; 7 | 8 | public ImageViewAttr(String imageViewId, String imageSrc, boolean isKey) { 9 | this.imageViewId = imageViewId; 10 | this.imageSrc = imageSrc; 11 | this.isKey = isKey; 12 | } 13 | 14 | public String getImageViewId() { 15 | return imageViewId; 16 | } 17 | 18 | public void setImageViewId(String imageViewId) { 19 | this.imageViewId = imageViewId; 20 | } 21 | 22 | public String getImageSrc() { 23 | return imageSrc; 24 | } 25 | 26 | public void setImageSrc(String imageSrc) { 27 | this.imageSrc = imageSrc; 28 | } 29 | 30 | public boolean isKey() { 31 | return isKey; 32 | } 33 | 34 | public void setKey(boolean key) { 35 | isKey = key; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /JavaTimeLine/src/main/java/io/github/rangaofei/javatimeline/viewattr/TextViewAttr.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.viewattr; 2 | 3 | 4 | import io.github.rangaofei.libannotations.TimeLineTextView; 5 | 6 | public class TextViewAttr { 7 | private String textViewId; 8 | private String textString; 9 | private boolean isKey; 10 | private String styleId; 11 | private String styleAnchorId; 12 | 13 | public TextViewAttr(String textString, TimeLineTextView textView) { 14 | this.textViewId = textView.id(); 15 | this.textString = textString; 16 | this.isKey = textView.key(); 17 | this.styleId = textView.style(); 18 | this.styleAnchorId = textView.styleAnchor(); 19 | } 20 | 21 | public String getTextViewId() { 22 | return textViewId; 23 | } 24 | 25 | public void setTextViewId(String textViewId) { 26 | this.textViewId = textViewId; 27 | } 28 | 29 | public String getTextString() { 30 | return textString; 31 | } 32 | 33 | public void setTextString(String textString) { 34 | this.textString = textString; 35 | } 36 | 37 | 38 | public boolean isKey() { 39 | return isKey; 40 | } 41 | 42 | public void setKey(boolean key) { 43 | isKey = key; 44 | } 45 | 46 | public String getStyleId() { 47 | return styleId; 48 | } 49 | 50 | public void setStyleId(String styleId) { 51 | this.styleId = styleId; 52 | } 53 | 54 | public String getStyleAnchorId() { 55 | return styleAnchorId; 56 | } 57 | 58 | public void setStyleAnchorId(String styleAnchorId) { 59 | this.styleAnchorId = styleAnchorId; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /JavaTimeLine/src/test/java/io/github/rangaofei/javatimeline/utils/StringUtilTest.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.javatimeline.utils; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | public class StringUtilTest { 8 | private String test1 = "R.layout.txt"; 9 | private String test2 = "R.ll.123"; 10 | 11 | private String test3 = "R.id.ttt"; 12 | private String test4 = "R.ii.123"; 13 | 14 | @Test 15 | public void isLayoutId() { 16 | boolean b = StringUtil.isLayoutId(test1); 17 | assertTrue(b); 18 | 19 | boolean b1 = StringUtil.isLayoutId(test2); 20 | assertFalse(b1); 21 | } 22 | 23 | @Test 24 | public void isResId() { 25 | boolean b = StringUtil.isResId(test3); 26 | assertTrue(b); 27 | 28 | boolean b1 = StringUtil.isResId(test4); 29 | assertFalse(b1); 30 | } 31 | } -------------------------------------------------------------------------------- /Pics/Screenshot_1532504922.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/Screenshot_1532504922.png -------------------------------------------------------------------------------- /Pics/SimpleStepViewUnnormal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/SimpleStepViewUnnormal.png -------------------------------------------------------------------------------- /Pics/StepViewAnim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/StepViewAnim.gif -------------------------------------------------------------------------------- /Pics/StepViewCustomDrawable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/StepViewCustomDrawable.png -------------------------------------------------------------------------------- /Pics/StepViewDiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/StepViewDiff.png -------------------------------------------------------------------------------- /Pics/StepView_anim.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/StepView_anim.webm -------------------------------------------------------------------------------- /Pics/eight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/eight.png -------------------------------------------------------------------------------- /Pics/muke.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/muke.mp4 -------------------------------------------------------------------------------- /Pics/muke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/muke.png -------------------------------------------------------------------------------- /Pics/simple_stepview_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/simple_stepview_all.png -------------------------------------------------------------------------------- /Pics/simple_stepview_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/simple_stepview_left.png -------------------------------------------------------------------------------- /Pics/taobao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/taobao.png -------------------------------------------------------------------------------- /Pics/wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangaofei/TimeLine/3a21f5b3b72d1f4f0764b66b5006d8596e0e1138/Pics/wechat.jpg -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "io.github.rangaofei.timeline" 7 | minSdkVersion 19 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | 13 | javaCompileOptions { 14 | annotationProcessorOptions.includeCompileClasspath = true 15 | } 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | dataBinding { 25 | enabled = true 26 | } 27 | 28 | 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(include: ['*.jar'], dir: 'libs') 33 | implementation 'com.android.support:appcompat-v7:27.1.1' 34 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 35 | implementation 'com.android.support:support-v4:27.1.1' 36 | testImplementation 'junit:junit:4.12' 37 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 38 | implementation 'com.android.support:recyclerview-v7:27.1.1' 39 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 40 | implementation project(path: ':sakatimeline') 41 | annotationProcessor project(path: ':JavaTimeLine') 42 | // implementation 'com.rangaofei:sakatimeline:0.2.4' 43 | // annotationProcessor 'com.rangaofei:JavaTimeLine:0.2.4' 44 | implementation 'com.android.support:cardview-v7:27.1.1' 45 | } 46 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/github/rangaofei/timeline/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("io.github.rangaofei.timeline", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/BaseModel.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.support.annotation.DrawableRes; 6 | import android.support.annotation.NonNull; 7 | 8 | import io.github.rangaofei.libannotations.TimeLine; 9 | import io.github.rangaofei.libannotations.TimeLineImageView; 10 | import io.github.rangaofei.libannotations.TimeLineTextView; 11 | 12 | 13 | @TimeLine(name = "BaseModelAdapter", keyLayoutId = "R.layout.item_key", valueLayoutId = "R.layout.item_value") 14 | public class BaseModel implements Parcelable, Comparable { 15 | 16 | @TimeLineTextView(id = "R.id.key") 17 | public String key; 18 | 19 | @TimeLineTextView(key = false, id = "R.id.value") 20 | public String value; 21 | 22 | @DrawableRes 23 | @TimeLineImageView(key = false, id = "R.id.iv") 24 | public int imageViewId; 25 | 26 | public BaseModel() { 27 | } 28 | 29 | public BaseModel(String key, String value, int imageViewId) { 30 | this.key = key; 31 | this.value = value; 32 | this.imageViewId = imageViewId; 33 | } 34 | 35 | public String getKey() { 36 | return key; 37 | } 38 | 39 | public void setKey(String key) { 40 | this.key = key; 41 | } 42 | 43 | public String getValue() { 44 | return value; 45 | } 46 | 47 | public void setValue(String value) { 48 | this.value = value; 49 | } 50 | 51 | 52 | @Override 53 | public String toString() { 54 | return "BaseModel{" + 55 | "key='" + key + '\'' + 56 | ", value='" + value + '\'' + 57 | '}'; 58 | } 59 | 60 | 61 | @Override 62 | public int describeContents() { 63 | return 0; 64 | } 65 | 66 | @Override 67 | public void writeToParcel(Parcel dest, int flags) { 68 | dest.writeString(this.key); 69 | dest.writeString(this.value); 70 | } 71 | 72 | protected BaseModel(Parcel in) { 73 | this.key = in.readString(); 74 | this.value = in.readString(); 75 | } 76 | 77 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 78 | @Override 79 | public BaseModel createFromParcel(Parcel source) { 80 | return new BaseModel(source); 81 | } 82 | 83 | @Override 84 | public BaseModel[] newArray(int size) { 85 | return new BaseModel[size]; 86 | } 87 | }; 88 | 89 | @Override 90 | public int compareTo(@NonNull BaseModel o) { 91 | if (o.key == null) { 92 | return 1; 93 | } else if (this.key == null) { 94 | return -1; 95 | } else { 96 | return this.key.compareToIgnoreCase(o.key); 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/MainActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.Toast; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import io.github.rangaofei.libannotations.TimeLineDividerAdapter; 15 | import io.github.rangaofei.sakatimeline.TimeLineView; 16 | import io.github.rangaofei.sakatimeline.adapter.AbstractTimeLineAdapter; 17 | import io.github.rangaofei.sakatimeline.adapter.ItemClickListener; 18 | import io.github.rangaofei.sakatimeline.divider.TimeLineType; 19 | import io.github.rangaofei.timeline.databinding.ActivityMainBinding; 20 | 21 | public class MainActivity extends AppCompatActivity { 22 | private ActivityMainBinding binding; 23 | 24 | 25 | private AbstractTimeLineAdapter secondAdapter; 26 | 27 | private List secondStepViewModels = new ArrayList<>(); 28 | 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 34 | binding.btn1.setOnClickListener(new View.OnClickListener() { 35 | @Override 36 | public void onClick(View v) { 37 | getSupportFragmentManager().beginTransaction() 38 | .replace(R.id.ll_container, TaobaoFragment.newInstance("1", "2")) 39 | .commit(); 40 | } 41 | }); 42 | binding.btn2.setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View v) { 45 | getSupportFragmentManager().beginTransaction() 46 | .replace(R.id.ll_container, MukeFragment.newInstance("1", "2")) 47 | .commit(); 48 | } 49 | }); 50 | initRecyclerView(); 51 | 52 | } 53 | 54 | 55 | private void initRecyclerView() { 56 | 57 | 58 | secondStepViewModels.add(new StepViewModel("许愿日", true, "04/25")); 59 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 60 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 61 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 62 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 63 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 64 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 65 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 66 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 67 | secondStepViewModels.add(new StepViewModel("签约日", false, "04/25")); 68 | 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/MukeBean.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | 4 | import io.github.rangaofei.libannotations.TimeLine; 5 | import io.github.rangaofei.libannotations.TimeLineTextView; 6 | 7 | @TimeLine(valueLayoutId = "R.layout.item_muke") 8 | public class MukeBean { 9 | @TimeLineTextView(key = false, id = "R.id.time") 10 | public String time; 11 | @TimeLineTextView(key = false, id = "R.id.content") 12 | public String content; 13 | 14 | public MukeBean(String time, String content) { 15 | this.time = time; 16 | this.content = content; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/MukeFragment.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import io.github.rangaofei.sakatimeline.TimeLineView; 14 | import io.github.rangaofei.sakatimeline.adapter.AbstractTimeLineAdapter; 15 | import io.github.rangaofei.sakatimeline.config.TimeLineConfig; 16 | import io.github.rangaofei.sakatimeline.divider.TimeLineType; 17 | 18 | 19 | /** 20 | * A simple {@link Fragment} subclass. 21 | * Use the {@link MukeFragment#newInstance} factory method to 22 | * create an instance of this fragment. 23 | */ 24 | public class MukeFragment extends Fragment { 25 | 26 | private static final String ARG_PARAM1 = "param1"; 27 | private static final String ARG_PARAM2 = "param2"; 28 | 29 | // TODO: Rename and change types of parameters 30 | private String mParam1; 31 | private String mParam2; 32 | 33 | private TimeLineView timeLineView; 34 | private List list = new ArrayList<>(); 35 | private AbstractTimeLineAdapter adapter; 36 | 37 | public MukeFragment() { 38 | 39 | } 40 | 41 | /** 42 | * Use this factory method to create a new instance of 43 | * this fragment using the provided parameters. 44 | * 45 | * @param param1 Parameter 1. 46 | * @param param2 Parameter 2. 47 | * @return A new instance of fragment MukeFragment. 48 | */ 49 | // TODO: Rename and change types and number of parameters 50 | public static MukeFragment newInstance(String param1, String param2) { 51 | MukeFragment fragment = new MukeFragment(); 52 | Bundle args = new Bundle(); 53 | args.putString(ARG_PARAM1, param1); 54 | args.putString(ARG_PARAM2, param2); 55 | fragment.setArguments(args); 56 | return fragment; 57 | } 58 | 59 | @Override 60 | public void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | if (getArguments() != null) { 63 | mParam1 = getArguments().getString(ARG_PARAM1); 64 | mParam2 = getArguments().getString(ARG_PARAM2); 65 | } 66 | } 67 | 68 | @Override 69 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 70 | Bundle savedInstanceState) { 71 | // Inflate the layout for this fragment 72 | View view = inflater.inflate(R.layout.fragment_muke, container, false); 73 | timeLineView = view.findViewById(R.id.muke_tlv); 74 | initView(); 75 | return view; 76 | } 77 | 78 | private void initView() { 79 | list.add(new MukeBean("5月1日", "我开始饿")); 80 | list.add(new MukeBean("5月2日", "我吃饭了")); 81 | list.add(new MukeBean("5月3日", "我吃饱了")); 82 | list.add(new MukeBean("5月4日", "我")); 83 | list.add(new MukeBean("5月5日", "我长胖了")); 84 | list.add(new MukeBean("5月6日", "我要减肥了")); 85 | list.add(new MukeBean("5月7日", "我只吃蔬菜")); 86 | list.add(new MukeBean("5月8日", "我好想吃肉")); 87 | list.add(new MukeBean("5月9日", "我吃肉了")); 88 | adapter = new MukeBeanAdapter(list); 89 | 90 | timeLineView.setTimeLineConfig(adapter, TimeLineType.StepViewType.LEFT_STEP_PROGRESS, 0); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/StepViewModel.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | 4 | import io.github.rangaofei.libannotations.TimeLine; 5 | import io.github.rangaofei.libannotations.TimeLineAnchor; 6 | import io.github.rangaofei.libannotations.TimeLineImageView; 7 | import io.github.rangaofei.libannotations.TimeLineTextView; 8 | 9 | @TimeLine(valueLayoutId = "R.layout.item_value") 10 | public class StepViewModel { 11 | 12 | @TimeLineTextView(key = false, id = "R.id.value", style = "R.style.StepView1", styleAnchor = "R.style.StepView2") 13 | public String text; 14 | @TimeLineAnchor({"R.id.value", "R.id.time"}) 15 | public boolean right; 16 | 17 | @TimeLineTextView(key = false, id = "R.id.time", style = "R.style.StepView1",styleAnchor= "R.style.StepView3") 18 | public String time; 19 | 20 | @TimeLineImageView(key = false, id = "R.id.iv") 21 | public int imgSrc; 22 | 23 | public StepViewModel(String text, boolean right, String time, int imgSrc) { 24 | this.text = text; 25 | this.right = right; 26 | this.time = time; 27 | this.imgSrc = imgSrc; 28 | } 29 | 30 | public StepViewModel(String text, boolean right, String time) { 31 | this.text = text; 32 | this.right = right; 33 | this.time = time; 34 | } 35 | 36 | public StepViewModel(String text, boolean right) { 37 | this.text = text; 38 | this.right = right; 39 | } 40 | 41 | public String getText() { 42 | return text; 43 | } 44 | 45 | public void setText(String text) { 46 | this.text = text; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/SwipeAdapter.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | import java.util.List; 12 | 13 | public class SwipeAdapter extends RecyclerView.Adapter { 14 | 15 | private List data; 16 | 17 | public SwipeAdapter(List data) { 18 | this.data = data; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | public SwipeAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 24 | LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 25 | View view = inflater.inflate(R.layout.item_swipe, parent, false); 26 | return new ViewHolder(view); 27 | } 28 | 29 | @Override 30 | public void onBindViewHolder(@NonNull SwipeAdapter.ViewHolder holder, int position) { 31 | holder.textView.setText(data.get(position)); 32 | } 33 | 34 | @Override 35 | public int getItemCount() { 36 | return data.size(); 37 | } 38 | 39 | public class ViewHolder extends RecyclerView.ViewHolder { 40 | private TextView textView; 41 | private TextView button; 42 | 43 | public ViewHolder(View itemView) { 44 | super(itemView); 45 | textView = itemView.findViewById(R.id.tv); 46 | button = itemView.findViewById(R.id.btn_delete); 47 | 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/SwipeRecyclerViewActivity.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.LinearSnapHelper; 7 | import android.support.v7.widget.PagerSnapHelper; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.support.v7.widget.helper.ItemTouchHelper; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import io.github.rangaofei.sakatimeline.divider.SakaItemTouchHelper; 15 | import io.github.rangaofei.sakatimeline.divider.SlideTouchHelperCallBack; 16 | 17 | public class SwipeRecyclerViewActivity extends AppCompatActivity { 18 | 19 | private RecyclerView recyclerView; 20 | private List data = new ArrayList<>(); 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_swipe_recycler_view); 26 | recyclerView = findViewById(R.id.swipe_rv); 27 | for (int i = 0; i < 20; i++) { 28 | data.add(String.valueOf(i)); 29 | } 30 | recyclerView.setAdapter(new SwipeAdapter(data)); 31 | recyclerView.setLayoutManager( 32 | new LinearLayoutManager(this)); 33 | LinearSnapHelper snapHelper = new LinearSnapHelper(); 34 | snapHelper.attachToRecyclerView(recyclerView); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/TaobaoFragment.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | import io.github.rangaofei.sakatimeline.TimeLineView; 15 | import io.github.rangaofei.sakatimeline.adapter.AbstractTimeLineAdapter; 16 | import io.github.rangaofei.sakatimeline.divider.TimeLineType; 17 | 18 | 19 | public class TaobaoFragment extends Fragment { 20 | 21 | private static final String ARG_PARAM1 = "param1"; 22 | private static final String ARG_PARAM2 = "param2"; 23 | 24 | private AbstractTimeLineAdapter firstAdapter; 25 | 26 | private TimeLineView timeLineView; 27 | private List list; 28 | private List firstStepViewModels = new ArrayList<>(); 29 | 30 | // TODO: Rename and change types of parameters 31 | private String mParam1; 32 | private String mParam2; 33 | 34 | 35 | public TaobaoFragment() { 36 | // Required empty public constructor 37 | } 38 | 39 | public static TaobaoFragment newInstance(String param1, String param2) { 40 | TaobaoFragment fragment = new TaobaoFragment(); 41 | Bundle args = new Bundle(); 42 | args.putString(ARG_PARAM1, param1); 43 | args.putString(ARG_PARAM2, param2); 44 | fragment.setArguments(args); 45 | return fragment; 46 | } 47 | 48 | @Override 49 | public void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | if (getArguments() != null) { 52 | mParam1 = getArguments().getString(ARG_PARAM1); 53 | mParam2 = getArguments().getString(ARG_PARAM2); 54 | } 55 | } 56 | 57 | @Override 58 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 59 | Bundle savedInstanceState) { 60 | // Inflate the layout for this fragment 61 | View view = inflater.inflate(R.layout.fragment_blank, container, false); 62 | timeLineView = view.findViewById(R.id.taobao_tlv); 63 | initView(); 64 | return view; 65 | } 66 | 67 | private void initView() { 68 | 69 | firstStepViewModels.add(new StepViewModel("卖家退款", false, "2016年5月1日")); 70 | firstStepViewModels.add(new StepViewModel("银行处理", false, "2016年6月1日")); 71 | firstStepViewModels.add(new StepViewModel("退款成功", false, "2016年6月1日")); 72 | firstAdapter = new StepViewModelAdapter(firstStepViewModels); 73 | list = new ArrayList<>(); 74 | list.add(getResources().getDrawable(R.drawable.withpadding)); 75 | list.add(getResources().getDrawable(R.drawable.withpadding)); 76 | list.add(getResources().getDrawable(R.drawable.ic_offline_pin_black_24dp)); 77 | timeLineView.setTimeLineConfig(firstAdapter, TimeLineType.StepViewType.TOP_STEP_PROGRESS, 3f, list); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/rangaofei/timeline/TimeLineAdapter.java: -------------------------------------------------------------------------------- 1 | package io.github.rangaofei.timeline; 2 | 3 | import android.widget.TextView; 4 | 5 | import java.util.List; 6 | 7 | import io.github.rangaofei.sakatimeline.adapter.AbstractTimeLineAdapter; 8 | import io.github.rangaofei.sakatimeline.adapter.BaseViewHolder; 9 | 10 | public class TimeLineAdapter extends AbstractTimeLineAdapter { 11 | public TimeLineAdapter(List list) { 12 | super(list); 13 | } 14 | 15 | @Override 16 | public int getKeyLayoutId() { 17 | return R.layout.item_key; 18 | } 19 | 20 | @Override 21 | public int getValueLayoutId() { 22 | return R.layout.item_value; 23 | } 24 | 25 | @Override 26 | public void bindKeyItem(BaseViewHolder holder, BaseModel baseModel) { 27 | ((TextView) (((KeyViewHolder) holder).itemView.findViewById(R.id.key))).setText(baseModel.getKey()); 28 | } 29 | 30 | @Override 31 | public void bindValueItem(BaseViewHolder holder, BaseModel baseModel) { 32 | ((TextView) (((ValueViewHolder) holder).itemView.findViewById(R.id.value))).setText(baseModel.getValue()); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_rect.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dot_grey.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_offline_pin_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_order.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/withpadding.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 12 | 13 | 19 | 20 | 23 | 24 |